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
47
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
150
グラフィカルに URL を編集できるサービスを作った
koharakazuya
1
15k
Go でのエラー生成パターン
koharakazuya
0
15k
Featured
See All Featured
Navigating Team Friction
lara
183
15k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7k
Automating Front-end Workflow
addyosmani
1366
200k
Git: the NoSQL Database
bkeepers
PRO
427
64k
Designing on Purpose - Digital PM Summit 2013
jponch
116
7k
Facilitating Awesome Meetings
lara
50
6.1k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
48
2.2k
Scaling GitHub
holman
458
140k
The MySQL Ecosystem @ GitHub 2015
samlambert
250
12k
GraphQLの誤解/rethinking-graphql
sonatard
67
10k
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
Rails Girls Zürich Keynote
gr2m
94
13k
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