Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
カリー化はナンの役に立つのか
Search
fsubal
February 08, 2019
Programming
9.3k
27
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
カリー化はナンの役に立つのか
昔ニコナレにアップロードしていたピクシブ社内勉強会資料です。
fsubal
February 08, 2019
More Decks by fsubal
See All by fsubal
負債になりにくいCSSをデザイナとつくるには?
fsubal
11
3.6k
Webデザインと フロントエンド技術🔰勉強会
fsubal
2
350
Tailwind CSSを本気でカスタマイズする方法
fsubal
17
7.9k
デザインシステムで Tailwind CSSとCSS in JSに分散投資をしたら良かった話
fsubal
20
7.5k
『Tailwind CSS実践入門』 出版記念基調講演
fsubal
9
5.4k
Sprockets CSSもやめる なぜ / Why stop using Sprockets for CSS too
fsubal
3
1.7k
The Majestic MPA
fsubal
8
3.4k
Backbone.Model に 型をつけて剥がす - Typing to destroy Backbone.Model
fsubal
1
1.2k
SVG + React でつくる レイヤーの自由変形 / Layer Transformation with React + SVG
fsubal
1
9.9k
Other Decks in Programming
See All in Programming
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
550
CSC307 Lecture 17
javiergs
PRO
0
320
Hunting Vulnerabilities in Symfony with LLMs
vinceamstoutz
0
320
開発体験を左右するライブラリの API 設計 - GraphQL スキーマ構築ライブラリから考える #tskaigi
izumin5210
2
1.7k
Technical Debt: Understanding it Rightly, Engaging it Rightly #LaravelLiveJP
shogogg
0
200
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
370
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
4.7k
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
240
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
130
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
440
ふつうのFeature Flag実践入門
irof
7
3.6k
Spring Security 実践 ─ GraphQL APIで実務に役立つ 認証・認可 を学ぶ
wagyu
0
190
Featured
See All Featured
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Paper Plane (Part 1)
katiecoart
PRO
0
8.7k
Visualization
eitanlees
152
17k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
44k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
140
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
610
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
380
Building Applications with DynamoDB
mza
96
7.1k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
Crafting Experiences
bethany
1
170
Accessibility Awareness
sabderemane
1
130
Transcript
カリー化は ナンの役に立つのか Web フロントエンド 編 pixiv Inc. f_subal 2018/12/07
2 誰 • pixivFACTORY フロントエンド • React と Redux と
SVG • 将来の夢は Single SVG Application です @f_subal
みなさん カリー化してますか 3
私は React + TS で 息をするように使ってます 4
「りろんはしってる」けど 5
「どう嬉しいのかわからない」 と言われがち 6
• カリー化を用いるとこんなことができる ◦ 読みやすい高階関数の building block をつくる ◦ 関数で DI
する ◦ 時間とともに変化する関数をつくる 7 話すこと
カリー化 (currying) 8
• n 引数関数を、n 回適用可能な 1 引数の連なりに変形すること • 例 ◦ (a:
number, b: number) => Math.max(a, b) が ◦ (a: number) => (b: number) => Math.max(a, b) になる ◦ a を渡すと、「b を渡すと number が返る関数」が返る 9 カリー化とは
// before function getMax(a: number, b: number) { return Math.max(a,
b) } // after (curried) function getMax(a: number) { return function (b: number) { return Math.max(a, b) } } 11
へー (何が嬉しいのかわからん) 11
たとえば .filter と合わせる 12
例題) number[] から 3 より 大きい要素だけを抽出せよ 13
// before function isLarger(a: number, b: number) { return a
> b } // with before const largerThanThrees = inputs.filter(function (n) { return isLarger(n, 3) }) 11
これでも悪くないが 15
// after (curried) function isLarger(a: number) { return function (b:
number) { return a < b } } // with after const largerThanThrees = inputs.filter(isLarger(3)) 11
// after (curried) function isLargerThan(a: number) { return function (b:
number) { return a < b } } // with after const largerThanThrees = inputs.filter(isLargerThan(3)) 11
// after (curried) function isLargerThan(a: number) { return function (b:
number) { return a < b } } // with after const largerThanThrees = inputs.filter(isLargerThan(3)) 11
いいじゃん 19
• .map .filter など、JS は高階関数 API を多用する • すると、高階関数にビジネスロジックとして意味のあるパターンが現れる ◦
.filter(isPremiumUser) とか ◦ .filter(isOwnedBy(user)) とか • こういう関数を切り出す際、カリー化しておくと便利 ◦ .map や .filter にドメインロジックが表現される ◦ 厳密に 1 引数ずつにしなくても「関数をかえす関数」にするだけでも便利 20 高階関数 の building block
• 多言語出身の人は .filter(isPremiumUser) より .selectPremiumUser() と書きたがる • A. 諦めろ ◦
経験上、クラスインスタンスにマッピングするメリットが、デメリットを上回ることはほぼ ない ◦ フロントエンドの殆どは「ユーザー入力を JSON に相互変換する処理」に過ぎない( 燃える発言 ) ◦ プレーンな関数に表現力を与える方向で議論したほうが有益( TypeScript はそれが 得意 ) 21 Q. オブジェクト指向すれば?
22
23
部分適用 (partial application) 24
• 関数の引数の一部だけ、予めある値を束縛しておくこと ◦ 例: (a, b) => a + b
があるとき、b=1 を部分適用すると increment 関数になる • カリー化された関数に対し、予め 1回(あるいは数回)呼んでおくことでも実現できる ◦ f が a => b => a + b のとき、f(1) すると b => b + 1 になる ◦ isOwnedBy(user) に currentUser を適用すると isMine(item) が作れる • カリー化と部分適用はよく混同される( 別の概念です ) 25 部分適用とは
部分適用したものを使いまわす 26
たとえば単位換算 27
例題 1) 100mm の直線が 1181px で表示されてる (≒ 300dpi) この時カーソルを 30px
動かすと、 何 mm 動いたことになる? 28
29
とりあえず カリー化無しで 書いてみる 30
// without currying // 1 inch === 25.4 mm function
px2mm(px: number, dpi: number) { return px / dpi * 25.4 } const dpi = 1181 / 100 * 25.4 const mm = px2mm(30, dpi) 11
これでもいい、が… 32
例題 2) 実はいま 1181 px で 表示されてるのはたまたまで、 100mm の原稿が window
の resize のたびに いろんな大きさ (px) になると仮定してください 33
例題 2) 実はいま 1181 px で 表示されてるのはたまたまで、 100mm の原稿が window
の resize のたびに いろんな大きさ (px) になると仮定してください 34
11
• (px, dpi) => mm という関数のうち、dpi が変わる場合がある ◦ dpi は「この
mm がこの px で表示されている」という事実から決定される • dpi は window をリサイズしない限り変わらない • 普通に扱う場合は、dpi を固定した状態で、px → mm 換算が行えると嬉しい 36 つまり…
• (px, dpi) => mm という関数を dpi => px =>
mm という形式で表すとどうなるか ◦ makePx2Mm に dpi を部分適用すると px2mm ができる • window.resize のたびに makePx2Mm(dpi) を実行すると… ◦ 「dpi をある値で固定したときの px → mm 換算関数」ができる!! 37 そこで部分適用ですよ
カリー化して解いてみる 38
// curried! // 1 inch === 25.4 mm function makePx2mm(dpi:
number) { return function (px: number) { return px / dpi * 25.4 } } 11
// in React Component class public componentDidMount() { document.addEventListener('resize', ()
=> { this.setState({ currentDpi: ... }) }) } get px2mm() { return makePx2mm(this.state.currentDpi) } 11
// with Hooks (>= v16.8.0) const [width, height] = useWindowSize()
const currentDpi = calcDpi(...) const px2mm = makePx2mm(currentDpi) 11
お分かりいただけただろうか 42
• ある時点において px ➔ mm はこういう式だが、別の時点では違う式になる • 外部要因( ここでは dpi
)を注入して関数を組み立てるような設計にしたい ◦ dpi は更に window size に依存する 43 時間経過によって変動する式
• 時間経過によって変化する対象( UI )をクラス / 関数で表す • そこに内部状態( React の
state )を閉じ込める • カリー化された関数に、内部状態を適用して式を組み立てる • あとはそれを子に引き回す( React なら Context API と合わせても良い ) ◦ 子は単に this.props.px2mm() すればよい(親の dpi は知らなくて良い) • 時間経過によって変わる関数式を使い回せて便利 44 Stateful な対象と合わせる
ところで 45
• その通り ◦ メソッドが1個しかないクラスに対する DI みたいなものだと思って • むしろ自分は DI をクラスで部分適用するものと見ている
◦ new Service(arg).call(hoge) はカリー化の OOP 的解釈 ◦ 関数だと呼出が 2 段以上続くケースも作りやすい ( service(arg)(hoge)(...) ) 46 これって DI みたいなもの?
• Q. 高階関数はともかくさ〜、if 文が ↓ みたいになるのイケてなくない? ◦ if ( isOwnedBy(user)(item)
) { • A. まぁそうですね ( uncurry しなければ ) • いま ECMAScript に提案中の pipeline 演算子がきたら ↓ みたいに書けるので辛抱してく ださい ◦ if ( item |> isOwnedBy(user) ) { 47 余談: Pipeline Operator
• カリー化を用いるとこんなことができる ◦ 読みやすい高階関数の building block をつくる ◦ 関数で DI
する ◦ 時間とともに変化する関数をつくる 48 まとめ