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
45
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
130
グラフィカルに URL を編集できるサービスを作った
koharakazuya
1
14k
Go でのエラー生成パターン
koharakazuya
0
14k
Featured
See All Featured
Ruby is Unlike a Banana
tanoku
96
10k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
227
16k
Making the Leap to Tech Lead
cromwellryan
124
8.5k
Practical Orchestrator
shlominoach
182
9.7k
Reflections from 52 weeks, 52 projects
jeffersonlam
345
19k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
25
2.3k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
34
8.9k
In The Pink: A Labor of Love
frogandcode
138
21k
The Mythical Team-Month
searls
216
42k
WebSockets: Embracing the real-time Web
robhawkes
59
7k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
17
1.4k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
120
39k
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