Slide 1

Slide 1 text

関数型プログラミングによる ロバスト(頑強) なコード設計 Yuki Toyoda (SGDC Bonifacio Team) Sansan VS サイボウズ - 品質向上Tips冬祭り

Slide 2

Slide 2 text

Bill Oneの話はすでにされているので…

Slide 3

Slide 3 text

SGDC

Slide 4

Slide 4 text

● Sansan Global Development Center ● フィリピン・セブ島にある開発組織。 ● 現在数十名のエンジニアが在籍している。 「組織で成果を最大化する。Sansanのグローバル 展開を加速させる2人のマネジャー」 https://jp.corp-sansan.com/mimi/interview-94.html SGDC

Slide 5

Slide 5 text

● 前提の整理 ● 副作用を意識する ● 宣言的プログラミングをする ● 型駆動設計をする 目次

Slide 6

Slide 6 text

前提の整理

Slide 7

Slide 7 text

● 致命的ではないのだが、「少しバージョンの古いJavaみのあるコードが 散見される」という個人的な課題感がある。 ○ 上手に型付けすればデータ構造がもっとシンプルになりそうな気が。 ○ 関数の中をもう少しモジュール化して、処理を小分けにして欲しい。 ○ 例外がナイーブにぽいぽい投げられている。たまに拾い損ねてる。 ○ そもそも関数のシグネチャを一目見ただけで何やってるか判別できて欲 しい。 ○ など。 ● 全部とは言わないが一部頑強でないかも。 (主観による)Bill Oneコードの課題

Slide 8

Slide 8 text

● リグレッションに強く、保守性が高い。 ○ 変数の取り違えなど妙な不具合が減る。 ○ 処理の単位が意味のある単位で細かくなるため、単体テストが書きやす い。 ● コードの意図をすばやく把握しやすい。 ○ 上から下にコードを読み下すだけで意図を把握できる。 ○ ないしは、そのコードブロックで何をしているかについて、コードを全 部読まなくても概ねわかるようになる。 ロバスト(頑強)なコードは…

Slide 9

Slide 9 text

副作用を意識する

Slide 10

Slide 10 text

● 関数型プログラミングにはいくつかの要素がある。 ○ 高階関数やカリー化 ○ 永続データ構造 ○ モナド ○ など。 ● ただ、中には関数型プログラミング言語の機能を利用しないと厳しいも のもある。 ● 他のプログラミング言語でもできることとして、「関数同士の合成」と 「純粋関数」に今回は着目する。 関数合成のために、副作用を意識する

Slide 11

Slide 11 text

● 「f(g(x))」のこと。 ● 「A を引数に受け取り、B を返す関数」と「B を引数に受け取り、C を 返す関数」を合成すると、「A を引数に受け取り、C を返す関数」にで きるという性質を示すと定義しておく。 ○ A→B、B→C⇒A→C 関数合成

Slide 12

Slide 12 text

● ただし、関数が副作用を生じるものであると合成結果を必ずしも信用で きなくなる。 ● 副作用のない関数同士、つまり純粋関数を合成したい。 関数合成と副作用、純粋関数

Slide 13

Slide 13 text

純粋でない関数の例

Slide 14

Slide 14 text

関数を純粋にした例

Slide 15

Slide 15 text

● 『Grokking Simplicity』という本には、書いているコードを次のように 分類して考えてみると良いという話が書かれている。 ○ Actions: 副作用を生じる関数 ○ Calculations: 純粋関数 ○ Data: 起こった出来事を記録しておく何か ● 使えるフレームワークとして、念頭に置いておきたい。 副作用をどこに生じているかを意識しながらコードを書く

Slide 16

Slide 16 text

宣言的プログラミングをする

Slide 17

Slide 17 text

● 宣言的プログラミング ○ Howではなく、Whatを明示しながらコーディングする方法。 ○ トップダウンにコードを書く。 ○ vs 命令的プログラミング 宣言的プログラミングを活用しよう

Slide 18

Slide 18 text

宣言的プログラミングを活用しよう https://x.com/gethackteam/status/1268892357027663873

Slide 19

Slide 19 text

宣言的なコード vs 命令的なコード 命令的なフィボナッチ数列の導出

Slide 20

Slide 20 text

宣言的なコード vs 命令的なコード 宣言的なフィボナッチ数列の導出

Slide 21

Slide 21 text

● 宣言的なプログラミングの第一歩として、コンビネータの利用が考えら れる。 ○ コンビネータとは、`filter`や`map`、`flatMap`などを指している。 ● コンビネータを呼び出すだけで、何の操作をしているかが一目でわかる。 ○ `filter`: 何かしらのフィルター処理をかけている ○ `map`、`flatMap`: 何かしらの変形処理をかけている ○ `take`: リストからn個の要素を取り出している ○ など。 コンビネータの利用

Slide 22

Slide 22 text

型駆動設計をする

Slide 23

Slide 23 text

● コードのデザインをする際に、型付けから考えていこうというもの。 ● 「どのような型遷移をさせるか?」から設計していく。 型駆動設計

Slide 24

Slide 24 text

● 型は論理式になりうる(*注)。そして、論理的な誤りはコンパイル時に 多く検出できる。 ● 関数も結局のところ型遷移なのだから、型付けを先に決めておくと宣言 的プログラミングの要素を活用しやすくなる。 ● 論理を使ってコードをロバストにすることができる。 *注: 主には当日のセッションを生で聞いていない方向け。いろんな前提や説明をすっ飛ばしている厳密な議論では ないので注意。念頭にあるのは、「カリー=ハワード同型対応」という、論理とプログラミングの間の関係性を示 したものである。 型付けを重視する理由

Slide 25

Slide 25 text

● できうる限り個々の事象を型に落とせないかを考える。 ○ 何らかの状態 ○ 何らかの属性 ○ 何らかの処理それ自体 ○ 例外 型付け時に何を意識するか

Slide 26

Slide 26 text

● たとえば下記のような要件があったとする。 ○ 注文処理が走った後、注文情報が正しいかを確かめる。その後、システ ム内部で正しかった注文を承認させる。承認後、ユーザーからのキャン セルなどがなければ注文を確定させる。 ● 次のような設計方法が考えられる。 ○ 型駆動設計したコード: Order、ValidatedOrder、AcknowledgedOrder、 PlacedOrderという型を用意する。 ○ さらにそれぞれを生成させるための関数を用意する。 注文処理の例を通じて型駆動設計を理解する

Slide 27

Slide 27 text

Slide 28

Slide 28 text

Slide 29

Slide 29 text

● 例外は型がつかないが、これは典型的な副作用であり、コードの論理体 系を破壊する。 ● Result型を使って例外の型付けを明示的にさせつつ、ユーザーにどちら の状態にいるかや処理の中断を意識させないプログラミング手法を 「Railway-Oriented Programming」と呼ぶ。 ● この際、flatMapを組み合わせて、かつ個々の関数を中で呼び出すだけで よくなる。こうした実装をアプリケーション全体で繰り返すイメージ。 例外すらも型付けできる

Slide 30

Slide 30 text

Railway-Oriented Programming

Slide 31

Slide 31 text

Railway-Oriented Programming

Slide 32

Slide 32 text

● 下記を使って、Java みのあるあんまりロバストでない Bill One のコー ドを潰していきたい。 ○ コードのどこに副作用が生じているのかを常に意識する。 ○ 宣言的プログラミングを活用する。 ○ 型駆動設計を意識しよう。 まとめ

Slide 33

Slide 33 text

No content