Slide 1

Slide 1 text

C#でわかる こわくないM 2017.5.27 GIFSHARP #1 KOUJI MATSUI (@KEKYO2)

Slide 2

Slide 2 text

Kouji Matsui - kekyo • NAGOYA city, AICHI pref., JP • Twitter – @kekyo2 / Facebook • ux-spiral corporation • Microsoft Most Valuable Professional VS and DevTech 2015- • Certified Scrum master / Scrum product owner • Center CLR organizer. • .NET/C#/F#/IL/metaprogramming or like… • Bike rider

Slide 3

Slide 3 text

はじめに •ずっと思考していたことを、どうやって自分 の言葉で表現するかを考えていた。今日はそ のアウトプットです。 •C#書ける人にリーチできるように考えました。 •本日が「岐阜Sharp」であることは、もちろん 承知しております ☺

Slide 4

Slide 4 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 5

Slide 5 text

事の始まり

Slide 6

Slide 6 text

事の始まり

Slide 7

Slide 7 text

事の始まり

Slide 8

Slide 8 text

事の始まり 分かりやすい課題だし、 ここからやるのが良かろう… ※Nullの議論や考察の深掘りは、それだけで沼であり、本題か ら外れるのでここでは扱いません。例えば、.NETのNullable はどうなのかとか。

Slide 9

Slide 9 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 10

Slide 10 text

Nullを安全になんとかしたい 例がアレだが… ジェネリックじゃない辞書

Slide 11

Slide 11 text

Nullを安全になんとかしたい

Slide 12

Slide 12 text

Nullを安全になんとかしたい .NET Frameworkの進化: • Dictionary : .NET 2.0で追加された。 • bool TryGetValue(TKey key, out TValue value) boolの判定を強制させることで、 失敗についてコード化させる。 が、モヤモヤする…

Slide 13

Slide 13 text

Nullを安全になんとかしたい 値が存在する場合だけ、コールバック関数を実行したらどうか。 こういうのを 作っておき…

Slide 14

Slide 14 text

Nullを安全になんとかしたい 値が存在する場合だけ、コールバック関数を実行したらどうか。 値が存在しない場合は 単に無視され実行されない

Slide 15

Slide 15 text

値を安全に操作したい 辞書の例を一般化して、 任意の参照型インスタンスを 安全に操作したい。 入れ物(ValueHolder)に 入れておき、Nullチェックする

Slide 16

Slide 16 text

値を安全に操作したい 値がnullの場合は 無視される

Slide 17

Slide 17 text

値を安全に操作したい これだと一回の操作ですべてが終わるので、ありがたみがない。 実際には値に対して複数の処理を連鎖的に実行したいはず。そこで: 戻り値を返す関数 を指定できる 関数を実行して 戻り値を返す

Slide 18

Slide 18 text

値を安全に操作したい 安全に操作できる 何か違う… 何でNullを手動で判定 しているんだ

Slide 19

Slide 19 text

TryExecuteの戻り値はT型なので、そこから先はNull検査が必要。 では、その値をValueHolderに入れれば良いのでは? 値を安全に操作したい 処理が連鎖出来るが、 いちいち入れ直す 必要がある モヤる

Slide 20

Slide 20 text

値を安全に操作したい ValueHolderで ラップして返す ターミネーション 処理用

Slide 21

Slide 21 text

値を安全に操作したい 実行される 実行されない

Slide 22

Slide 22 text

値を安全に操作したい 途中からNullになると 以降は安全に無視

Slide 23

Slide 23 text

値を安全に操作したい こんなユーティリティ を作っておく

Slide 24

Slide 24 text

値を安全に操作したい 定義が楽になる

Slide 25

Slide 25 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 26

Slide 26 text

ネストしたNullの安全な処理 関数内でもValueHolderを使いたいかもしれない: Func

Slide 27

Slide 27 text

ネストしたNullの安全な処理 Funcから Func>に変更 もうラップしなくても 良くなった

Slide 28

Slide 28 text

ネストしたNullの安全な処理 今度はこっちがTをそのまま 返しているのでエラー

Slide 29

Slide 29 text

ネストしたNullの安全な処理 Someを使ってラップして返すと ValueHolderとなって辻褄が合う

Slide 30

Slide 30 text

ネストしたNullの安全な処理 我々は、これを一般的に「Option型」呼んでいます:

Slide 31

Slide 31 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 32

Slide 32 text

名前を変える ちょっと名前に馴染みが薄 いかもしれませんが: •“TryExecute”を”Bind” •”Some”を”Return” に変えます。

Slide 33

Slide 33 text

名前を変える これが、 「Optionモナド」 です。 ※あるいはMaybeモナドと言う場合もあります。 ※ツッコミが入りそうだから、ちょっとまって♡

Slide 34

Slide 34 text

モナドを構成するもの モナドは、Optionだけではなく、様々な種類のものがありますが、 ある構造を「モナド」と呼ぶには、以下の構造を持っている必要が あります: • 型構築子 : 値の型をジェネリックとして受け取ることが出来る、 Option型そのものの事です(.NET的に言うなら、オープンジェ ネリック型)。 • return関数 : Return関数の事です。Tの値をOptionに変換します • bind関数 : Bind関数の事です。Tの値を引数に取る関数を実行し、 Optionの値を返します Wikipedia [モナド(プログラミング)]

Slide 35

Slide 35 text

モナドを構成するもの 名称がぶれる事がある(returnをunitと呼ぶ場合など)のは、 処理系によって呼称がバラバラだからですが、モナドらしい構 造を持っていれば、名称自体は重要ではありません。 • Wikipediaでは、returnをunitとして説明しています。 • returnはHaskellのreturnから来ています。 • bindはflatMapと呼ぶ場合もあります。 ただし、会話するときには相手に通じないかもしれないので 注意したほうが良いでしょう。

Slide 36

Slide 36 text

モナドを構成するもの 名称以外にも、「モナドである」と言うには、以下の規則も 備えている必要があります: 1. return関数をそのままbind関数に適用すると、元の値のま まとなる: 要するにそのまま ラップして返している

Slide 37

Slide 37 text

モナドを構成するもの 「 return関数をそのままbind関数に適用」 を素直に書くと、より分かりやすい

Slide 38

Slide 38 text

モナドを構成するもの 2. ふたつの関数を続けてbindするのは、これらの関数から決 まるひとつの関数を bind することに等しい:

Slide 39

Slide 39 text

モナドを構成するもの “DEF”と”GHI”を計算する式を「先に」bindして、そ の式を”ABC”のOptionにbindしている: (A bind B) bind C ←同じ→ A bind (B bind C)

Slide 40

Slide 40 text

モナドを構成するもの 上記の規則のことを 「モナド則」 と言います。 ※一見してモナドのような形をした型があっても、この規則に合 致しない場合は、その型はモナドではありません。

Slide 41

Slide 41 text

Bindを正確に実装 ここまで述べたOption.Bindは、実は正確ではありません。 Optionを返す関数(?) 違う型(Option)を返し たくても、Tがstringなのでエラー

Slide 42

Slide 42 text

Bindを正確に実装 UをジェネリックとしてOptionを 返す関数にする。 関数がOptionを返しても正しく マッチする。 • bind関数 : Bind関数の事です。Tの値を引数に取る関数を実行し、 Optionの値を返します

Slide 43

Slide 43 text

その他 “TryExecute”に対応するものを調べたところ、”Match”と呼称す ることがありました。 これについてはまた後で取り上げますが、とりあえず以降の サンプルはMatchと表記します。

Slide 44

Slide 44 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 45

Slide 45 text

処理の連鎖 複雑な計算処理を安全に行おうとすると、Bindがネストするこ とがあたりまえになります: すべての値が存在する場合だけ、 合計を計算する

Slide 46

Slide 46 text

処理の連鎖 実はこの構造、驚くべきことにLINQのSelectManyにそっくりです: すべての値が存在する場合だけ、 合計を計算する

Slide 47

Slide 47 text

処理の連鎖 LINQで異なるところは: • Return(value)がnew[] { value } // 要素1個だけの配列の生成 • Bind()がSelectMany() // ネストしたリストのアンループ です。 ※最後の出力はMatchがないので、foreachで出力しています。 ※bindがflatMapと呼ばれる事から、SelectManyに近しい感じもします。

Slide 48

Slide 48 text

処理の連鎖 意味を考えてみると: 1. 要素1個だけの配列: 配列のインスタンス(int[])は常に存在し、要素が1個存在するか 又は存在しないか。 → 要素が1個存在する場合だけ処理 ≒ Nullではない場合だけ処理 Value Null [1] [0]

Slide 49

Slide 49 text

処理の連鎖 2. SelectManyで配列を返す: 結果が1個だけ格納された、又は要素が格納されていない配列のイ ンスタンスを返すことで、結果がNullかどうかを間接的に表現 3. 最終結果はIEnumerableだけど、要素が1個存在するか又は存在 しないか、のどちらかで表現される。

Slide 50

Slide 50 text

処理の連鎖 SelectManyに渡す関数のことを「継続」、このような形式を「継 続渡し」と呼びます: 継続となるラムダ式が3重にネストしている 継続はコールバックとみなせる

Slide 51

Slide 51 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 52

Slide 52 text

LINQクエリ構文 LINQと構造がそっくりということは、OptionもわざとLINQっぽ く定義することで、LINQのクエリ構文に対応できます: Bindに対応するSelectManyの実装 (邪魔なので)拡張メソッドで定義 引数が多少違うのは、LINQクエリ構文 を効率よく実行するためで、 Bindの呼び出し回数を削減する

Slide 53

Slide 53 text

LINQクエリ構文 すると、このようにシンプルに書けます: ここらへんがなんとなくbind bindが全て成功(有効な値) すると、計算してreturn

Slide 54

Slide 54 text

LINQクエリ構文 このクエリ構文は、シンプルに短く書けるものの、意味が分かり づらいことが問題です。 • fromがbindで、selectがreturnとは、想像しにくい。 つまり、LINQはあくまでコレクション(シーケンス)に対する操 作である(だからSQLっぽいキーワード)のに、その構文を全く別 の用途に応用しているからです。 ※私自身は、このように書けることにあまりメリットを見いだせ ていません…

Slide 55

Slide 55 text

LINQクエリ構文 ですが、なんとなく LINQがモナドの一種のように見える と言うのは分かりましたか? ※断定するのは去年あたりに自信がなくなって以来解決していな いので、やめておきます

Slide 56

Slide 56 text

ところでこの会は 岐阜Sharp (giFSharp) でしたね?

Slide 57

Slide 57 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 58

Slide 58 text

F#でのモナド 基本的に、C#で見せた構造と変わりません。 (サンプルコードはC#に最適化してありますが) ここでは、 「なぜC#ではなくF#を使いたいか?」 にフォーカスします。

Slide 59

Slide 59 text

F#でのOptionモナド F#でOptionモナドを使ってみます。F#標準のOption型と被るので ”Optional”と変更していますが、C#で書いたクラスをそのまま使えます: 書き方もC#とほとんど同じ

Slide 60

Slide 60 text

F#でのLINQ F#でのLINQも書き直して対比させてみます: 書き方もC#とほとんど同じ IEnumerableの共変性を考慮した 型推論が難しいので、ここだけ補助

Slide 61

Slide 61 text

F#でのシーケンス F#ではLINQ演算子を直接使うことはあまりなく、代わりに シーケンス(Seq)の演算子を使います: SelectMany (Bind) は Seq.collectに対応します Seqなら型推論出来るので、補助は不要

Slide 62

Slide 62 text

F#でのシーケンス しかし、シーケンスを使うなら、もっと良い方法があります。 シーケンス式です: ここらへんがSeq.collect (bind) bindが全て成功(有効な値) すると、計算してreturn C#でのLINQクエリ構文と そっくりです

Slide 63

Slide 63 text

F#でのシーケンス ここではシーケンス式自体はあまり掘り下げませんが、F#はこの 「ブロックで囲まれた式」をビルトインで定義しています: •シーケンス向き(Seq) : seq { … } •非同期処理向き(Async) : async { … } •DBクエリ向き(IQueryable) : query { … } そして、これらビルトインワークフロー以外にも: •独自の計算定義 : hogehoge { … } → コンピュテーション式

Slide 64

Slide 64 text

コンピュテーション式の導入 Optionalをコンピュテーション式で使えるようにします: 「ビルダークラス」を定義します。 最低限、”Return”と”Bind”を定義しますが、 既存のOptionalに転送するだけです このクラスのインスタンスを ”optional”と命名しておきます

Slide 65

Slide 65 text

コンピュテーション式の導入 OptionalBuilderとそのインスタンス”optional”を見えるスコープ に配置しておけば: optionalコンピュテーションブロックが 使用可能に!! let! (let-bang) でbindが実行される。 値が無効ならそれ以上評価されない (Optional.Bindが無視する) 自然で穏当な式表記 optionalで生成されたインスタンスは Optionalそのもの

Slide 66

Slide 66 text

コンピュテーション式の導入 C# LINQクエリ構文 F# カスタムコンピュテーション式 (&中身はOptionモナド)

Slide 67

Slide 67 text

コンピュテーション式の導入 コンピュテーション式の利点: • C# LINQクエリ構文と比べ、より自然で適した文法にしやすい。 使用可能なキーワードが多い。 let, do, return, for..do, try..finally, use, yieldなど、大体網羅出来る • ビルトインワークフロー群と同じ手法で拡張でき、LINQの制約 に縛られない。 • バックエンドはほぼモナドそのまま。 「Computation Expressions」 https://docs.microsoft.com/en- us/dotnet/fsharp/language-reference/computation-expressions

Slide 68

Slide 68 text

コンピュテーション式の導入 コンピュテーション式の欠点: • F#でしか使えない ☺

Slide 69

Slide 69 text

その他 ”Match”の由来が何かを説明するのを忘れていました。 F#には「判別共用体」があります。これを使って強力なパター ンマッチングが出来るのですが: F#のOption型(判別共用体) Some: 任意の型の値を保持 None: 値を保持しない パターンマッチング: OptionにはSomeかNoneしかありえない →網羅性の自動検査が出来る

Slide 70

Slide 70 text

その他 値が存在する・値が存在しない、の両方のパターンの処理を 書かせるため、Matchと呼称していると思われます。 値が存在する場合と存在しない場合の 両方の関数を必ず書かせることで 網羅性を担保する

Slide 71

Slide 71 text

Agenda 事の始まり Nullを安全になんとかしたい ネストしたNullの安全な処理 名前を変える 処理の連鎖 LINQクエリ 岐阜 まとめ

Slide 72

Slide 72 text

モナドとは? モナドとは: モナドは計算を表現する構造であり、計算ステップの列から なる。つまり、型がモナド構造をもつというのは、命令を繋げ るやり方、言い換えるとその型をもつ関数をネストさせる規則 が定まっていることをいう。 Wikipedia [モナド (プログラミング)]

Slide 73

Slide 73 text

モナドとは? C#での、モナドの適用動機: • 正直、あまりない(だから知識共有されない?)。 • LINQの演算子がモナドっぽいけど、普段意識することはない。 • 文(Statement : voidな手続き)とは相性が悪い。Bindに渡す関数が 値を返せない。 F#での、モナドの適用動機: • 関数型プログラミング言語では一般的に使われる概念(?)。全て の式が値を返すので、自然に導入できる。 • コンピュテーション式で自然に拡張・記述しやすく出来る

Slide 74

Slide 74 text

モナドはデザインパターンか? モナドがデザインパターンと呼べるかどうかわかりませんが: デザインパターン(または設計パターン)とは、過去のソフ トウェア設計者が発見し編み出した設計ノウハウを蓄積し、名 前をつけ、再利用しやすいように特定の規約に従ってカタログ 化したものである。 Wikipedia [デザインパターン]

Slide 75

Slide 75 text

モナドはプログラミングパラダイムか? モナドがプログラミングパラダイムと呼べるかどうかわかり ませんが: プログラミングパラダイムは、プログラマにプログラムの見 方を与えるものと言える。たとえば、オブジェクト指向プログ ラミングにおいて、プログラムとはオブジェクトをつくりそれ を管理するものである。関数型プログラミングにおいては、状 態を持たない関数の評価の連続である。 Wikipedia [プログラミングパラダイム]

Slide 76

Slide 76 text

謝辞 • 「モナドの脅威」 matarillo.com http://matarillo.com/general/monads.php • 「もしC#プログラマーがMaybeモナドを実装したら」 gab_km http://blog.livedoor.jp/gab_km/archives/1361759.html • 「Optionに見るコンピュテーション式のつくり方」 bleis-tift http://bleis-tift.hatenablog.com/entry/how-to-make- computation-expression

Slide 77

Slide 77 text

謝辞

Slide 78

Slide 78 text

Thanks join! The implementations --> GitHub: CSharpMonadic ◦ https://github.com/kekyo/CSharpMonadic/ My blog ◦ http://www.kekyo.net/