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
pickpatch を作った話と仕組みの解説
Search
KoharaKazuya
June 14, 2019
0
50
pickpatch を作った話と仕組みの解説
ランチタイム勉強会 2019-06-14
KoharaKazuya
June 14, 2019
Tweet
Share
More Decks by KoharaKazuya
See All by KoharaKazuya
Native File System API の紹介と Zip を作る Web サービスを作った話
koharakazuya
0
160
グラフィカルに URL を編集できるサービスを作った
koharakazuya
1
15k
Go でのエラー生成パターン
koharakazuya
0
16k
Featured
See All Featured
Become a Pro
speakerdeck
PRO
28
5.4k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
Being A Developer After 40
akosma
90
590k
Automating Front-end Workflow
addyosmani
1370
200k
Building an army of robots
kneath
306
45k
How to Ace a Technical Interview
jacobian
277
23k
How STYLIGHT went responsive
nonsquared
100
5.6k
How GitHub (no longer) Works
holman
314
140k
Rebuilding a faster, lazier Slack
samanthasiow
82
9.1k
Done Done
chrislema
184
16k
The Language of Interfaces
destraynor
158
25k
Making the Leap to Tech Lead
cromwellryan
134
9.4k
Transcript
pickpatch を作った話と仕組み の解説 ランチタイム勉強会
今日話すこと set("a.b", 10) のようなダサい API をやめるライブラリを書 いた ‑ Qiita pickpatch
の技術解説 (個人的に会心のアイデアだったので事あるごとに自慢してる 話) 2 2
自己紹介 小原 一哉 (こはら かず や) ウェブエンジニア フェンリル株式会社 @KoharaKazuya 3
3
背景 ネストの深いオブジェクトの一部分を簡単に更新したい const app = { title: "pickpatch", authors: [
{ age: 29, name: "Kohara Kazuya" } ] }; 4 4
既存の手法 代入 (破壊的変更、mutation) Underscore.js, Lodash Immer 5 5
代入 app.authors[0].name = " 小原 一哉"; 当然一番手軽で安定した方法。 (Redux の台頭など、詳細は割愛するが) Immutable
がいい場合は使えない。 6 6
Underscore.js, Lodash const replaced = _.set(app, "authors[0].name", " 小原 一哉");
文字列でアクセス。 静的解析と相性が悪い。TypeScript の台頭で露見してきた。 7 7
Immer const replaced = produce(app, draft => { draft.authors[0].name =
" 小原 一哉"; }); マジカルになんとかしちゃう。 書く側は代入と一緒だが、裏では一部を置き換えつつ 新しいオブジェクトを作っている。 8 8
Immer すごい 9 9
Immer の仕組み ES2015 Proxy を用いて実現している。 Proxy: オブジェクトへのアクセス、操作、命令を インターセプトできるやつ。 名前の通りプロキシのイメージ 10
10
Immer いいけど、そもそも代入っぽいこともしたくないんや。 const replaced = produce(app, draft => { draft.authors[0].name
= " 小原 一哉"; // ↑ ここが生理的に無理なんや }); 11 11
アイデア 値の更新とは新しい値の宣言 (reduce など) 更新したい部分の宣言と遷移の処理は分離できる 部分の宣言は . によるチェーンがもっとも自然 12 12
pickpatch を作った const obj = { a: { b: 1
}, c: 2, d: 3 }; const newObj = pickpatch( _ => [_.a.b, _.c], // picker defines partial to update )( ([b, c]) => [b * 10, c + 5], // patcher defines new values )( obj, // old object ); // -> { a: { b: 10 }, c: 7, d: 3 } 13 13
pickpatch の仕組み (一ファイルの数十行のソースだがやたら難しい) picker と patcher と更新対象を引数にとる Immer と同じく Proxy
を使ってマジカルなことをする 14 14
picker 更新対象から更新する部分を宣言させる関数 const picker = app => app.authors[0].name; ユーザーは 「更新対象の全体が引数として与えられて、
更新する部分を返す関数」 を定義する 15 15
picker 実際は picker の引数には Proxy が仕掛けられた ダミーのオブジェクトを渡している const picker =
app => app.authors[0].name; picker の戻り値にはプロパティアクセスの キーが記録されている 16 16
patcher 更新のロジックを定義する関数 const patcher = old => " 小原 一哉";
ユーザーは 「picker で選択した値が引数として与えられて、 その部分の新しい値を返す関数」 を定義する 17 17
patcher picker のプロパティアクセスのキーの情報を元に、 更新対象から値を抽出し、引数に与える。 const patcher = old => "
小原 一哉"; 返り値を更新の際に用いる。 更新対象の一部を置き換えた新しいオブジェクトを生成する。 18 18
Type Inference Fiendly 型定義はユーザーのメンタルモデルに合わせている ≠ 真実の型 → TypeScript でしっかり型チェックが効く 19
19
関連 後から知ったんだけど、類似の実装として Haskell Lens というものがあるらしい……。 読もうとして撃沈しました。 多分ユーザーのメンタルモデルは同じ。 20 20
まとめ pickpatch を作った Proxy を使ってマジカルなことができる 部分の宣言と更新ロジックの分離 Haskell Lens というのもあるらしい 21
21