F#を学んで感じた関数プログラミング
  習熟度レベル+FSharpx の Iteratee

        pocketberserker


        2012 年 9 月 1 日
自己紹介




• 中山 / なかやん
• @pocketberserker / id:pocketberserker
• どこにでもいるふつーのサーガ大学院生
• F# / Haskell / (Erlang) / TDD / テスト
• FSharpx の Contributor らしい…
私と関数型
私と関数型




初期の思い込み
私と関数型




関数型 = Lisp でしょ?
私と関数型




    関数型 = Lisp でしょ?
Lisp こわいから関数型こわい
結論から言えば
結論から言えば



• ただの食わず嫌いでした、ごめんなさい
結論から言えば



• ただの食わず嫌いでした、ごめんなさい
• 初めて参加した勉強会(TDDBC 名古屋)で
 思い違いを知る
結論から言えば



• ただの食わず嫌いでした、ごめんなさい
• 初めて参加した勉強会(TDDBC 名古屋)で
  思い違いを知る
• 気がついたら Java から F#に鞍替えしていた
結論から言えば



• ただの食わず嫌いでした、ごめんなさい
• 初めて参加した勉強会(TDDBC 名古屋)で
  思い違いを知る
• 気がついたら Java から F#に鞍替えしていた
• これがちょうど 2 年前の話
学んだり会話したりして感じたこと
学んだり会話したりして感じたこと




関数プログラミングにも習熟度レベルがある?
ざっくりと考えてみる
免責事項



• 人によって “関数プログラミング (FP)” の定義
  は異なる
• ここでは:基本副作用を許さず関数が第一級
  の値なら FP
• 動的型付け:わからないので今回は除外
想像してみた習熟度レベル(言語共通)




1. はろー再帰呼び出し
2. 再帰より高階関数
想像してみた習熟度レベル(静的型付け編)




 1. Option, Either
 2. モナドを使う
 3. モナド自作
はろー再帰呼び出し
はろー再帰呼び出し



• 基本的な部品の一つ
はろー再帰呼び出し



• 基本的な部品の一つ
• これを理解できないと(たぶん)後々つらい
はろー再帰呼び出し



• 基本的な部品の一つ
• これを理解できないと(たぶん)後々つらい
• でも再帰は最後の手段
はろー再帰呼び出し



• 基本的な部品の一つ
• これを理解できないと(たぶん)後々つらい
• でも再帰は最後の手段
• 不必要に多用しているうちは訓練不足
再帰より高階関数
再帰より高階関数


• 目的に特化した高階関数は fold 系でも書ける
再帰より高階関数


• 目的に特化した高階関数は fold 系でも書ける
• fold 系は再帰でも書ける
再帰より高階関数


• 目的に特化した高階関数は fold 系でも書ける
• fold 系は再帰でも書ける
• 再帰 > fold 系 > 目的に特化した高階関数 と
 いう力関係
再帰より高階関数


• 目的に特化した高階関数は fold 系でも書ける
• fold 系は再帰でも書ける
• 再帰 > fold 系 > 目的に特化した高階関数 と
  いう力関係
• 力の弱いほうを使ったほうがコードがわかり
  やすい
再帰より高階関数


• 目的に特化した高階関数は fold 系でも書ける
• fold 系は再帰でも書ける
• 再帰 > fold 系 > 目的に特化した高階関数 と
  いう力関係
• 力の弱いほうを使ったほうがコードがわかり
  やすい
• 力の弱いものを優先して使っていく
力の階層と節度




     関数プログラミングの道しるべ
“函数プログラミングの集い 2011” での山本さんの
          発表資料
高階関数になれるためにも・
            ・・
高階関数になれるためにも・
              ・・


• 訓練あるのみ
高階関数になれるためにも・
              ・・


• 訓練あるのみ
• 一度再帰で書いて、後でリファクタリングで
 きないか検討
高階関数になれるためにも・
              ・・


• 訓練あるのみ
• 一度再帰で書いて、後でリファクタリングで
  きないか検討
• そのうち一気に高階関数のほうで書けるよう
  になる
高階関数になれるためにも・
              ・・


• 訓練あるのみ
• 一度再帰で書いて、後でリファクタリングで
  きないか検討
• そのうち一気に高階関数のほうで書けるよう
  になる
• 注意:リファクタリングにはテストコード
  必須
ここから静的型付けの話
Option(Maybe), Either
Option(Maybe), Either



• 値が返ってこないかもしれない計算
Option(Maybe), Either



• 値が返ってこないかもしれない計算
• 失敗や例外を投げる計算
Option(Maybe), Either



• 値が返ってこないかもしれない計算
• 失敗や例外を投げる計算
• “カターンゼン!”
Option(Maybe), Either



• 値が返ってこないかもしれない計算
• 失敗や例外を投げる計算
• “カターンゼン!”
• モナドへの第一歩
モナドを使う
モナドを使う




• 使えなくても FP できる(純粋なものは別)
モナドを使う




• 使えなくても FP できる(純粋なものは別)
• でも使えたら便利!
モナドを使う




• 使えなくても FP できる(純粋なものは別)
• でも使えたら便利!
• 使うところからはじめましょう
モナド自作
モナド自作



• まだ見ぬ高み
モナド自作



• まだ見ぬ高み
• 先ほど dico leque さんが発表していたらしい
モナド自作



• まだ見ぬ高み
• 先ほど dico leque さんが発表していたらしい
• 発表聞いた人は感想教えてください!
モナド自作



• まだ見ぬ高み
• 先ほど dico leque さんが発表していたらしい
• 発表聞いた人は感想教えてください!
• と思ったけどハッシュタグ見てたら厳しい
 かも…
ここまでのまとめ



• FP には習熟度レベルがあるぽい
• 次に挑戦すべき段差が何か知ることができれ
  ば学びやすいのかも
• 一歩一歩進めばそのうち上達する
• こわくない
FSharpx の Iteratee
Iteratee
Iteratee



• I/O の表現の一つ
Iteratee



• I/O の表現の一つ
• データを読み込んでコールバック関数に渡す
Iteratee



• I/O の表現の一つ
• データを読み込んでコールバック関数に渡す
• データと処理の分離
Iteratee



• I/O の表現の一つ
• データを読み込んでコールバック関数に渡す
• データと処理の分離
• コンビネータ的な操作
ソースコードで説明してみる
注意




ここからの公用語は(もしかしたら)
       F#です
入力データを表す Stream     

 type Stream’a =
 | Chunk of ’a
 | Empty
 | EOF
                            


データ Chunk, 空 Empty, 終端 EOF
消費者 Iteratee             

 type Iteratee’el,’a =
 | Done of ’a * Stream’el
 | Error of exn
 | Continue of
   (Stream’el - Iteratee’el,’a)
                                      


処理終了状態 Done, 計算途中 Continue, エラー
Error
生産者 Enumerator   

 type Enumerator’el,’a =
   Iteratee’el,’a -
     Iteratee’el,’a
                            


Iteratee にデータを与える
パイプ Enumeratee               

 type Enumeratee’elo,’eli,’a =
   Iteratee’eli,’a -
     Iteratee’elo, Iteratee’eli,’a
                                        


enumerator からデータを受け取り Iteratee に渡す
疲れたのでちょっと休憩




            ことりちゃんマジかわいい本
画像は mzp さん、bleis さんの許可を得て掲載しています
ここからは




FSharpx での実装を使って使用方法の紹介
FSharpxって?
FSharpxって?



• Core にはない便利な関数の提供
FSharpxって?



• Core にはない便利な関数の提供
• Core, Http, Observable, TypeProviders
FSharpxって?



• Core にはない便利な関数の提供
• Core, Http, Observable, TypeProviders
• ちなみに F#er 内で賛否両論ある模様
FSharpxって?



• Core にはない便利な関数の提供
• Core, Http, Observable, TypeProviders
• ちなみに F#er 内で賛否両論ある模様
• 例:Core だけで既に重量級ライブラリ
FSharpx の Iteratee
FSharpx の Iteratee


• F#での Iteratee 実装
FSharpx の Iteratee


• F#での Iteratee 実装
• (∗ ) や ( ∗) も実装されている
FSharpx の Iteratee


• F#での Iteratee 実装
• (∗ ) や ( ∗) も実装されている
• Iteratee.List と Iteratee.Binary(ByteString)
FSharpx の Iteratee


• F#での Iteratee 実装
• (∗ ) や ( ∗) も実装されている
• Iteratee.List と Iteratee.Binary(ByteString)
• 欠点 1:Enumeratee が 1 つも実装されてい
  ない
FSharpx の Iteratee


• F#での Iteratee 実装
• (∗ ) や ( ∗) も実装されている
• Iteratee.List と Iteratee.Binary(ByteString)
• 欠点 1:Enumeratee が 1 つも実装されてい
  ない
• 欠点 2:Eumeratee と Iteratee を合成する関
  数がない
FSharpx の Iteratee


• F#での Iteratee 実装
• (∗ ) や ( ∗) も実装されている
• Iteratee.List と Iteratee.Binary(ByteString)
• 欠点 1:Enumeratee が 1 つも実装されてい
  ない
• 欠点 2:Eumeratee と Iteratee を合成する関
  数がない
FSharpx の Iteratee


 • F#での Iteratee 実装
 • (∗ ) や ( ∗) も実装されている
 • Iteratee.List と Iteratee.Binary(ByteString)
 • 欠点 1:Enumeratee が 1 つも実装されてい
   ない
 • 欠点 2:Eumeratee と Iteratee を合成する関
   数がない
だったのが一昨日までの話
おかげで資料を一部作り直すことに…
気持ちを切り替え



使ってみよう Enumerator - あどけない話
のネタを参考に F#でデモ(時間があれば)
Iteratee を作る                      

let rec consumer =
  iteratee {
    let! mw = head
    match mw with
    | None - ()
    | Some w -
      printf XXX 
      w
      | (ByteString.singleton  ByteString.toString)
      | printfn %s
      return! consumer
  }
                                                        


head はストリームを消費して要素を取り出す
Iteratee
実行その 1   

 run | consumer
Enumerator を作ってみる   

let listFeeder =
  [BS 12B; BS 34B]
  | enumList
実行その 2           

 run | listFeeder consumer
XXX 1
XXX 2
XXX 3
XXX 4
入力を増やす                

let fileFeeder =
  IO.readFile @.FILE
  | Seq.map ByteString.ofString
  | Seq.toList
  | enumList
実行その 3                    

  run | (fileFeeder  listFeeder) consumer
 XXX 1
 XXX 2
 XXX 3
 XXX 4
 XXX 5
 XXX 6
 XXX 7
 XXX 8
お仕事を増やす                            

let consumer2 =
  iteratee {
    let! mw = head
    match mw with
    | None - ()
    | Some w -
      printf YYY 
      w
      | (ByteString.singleton  ByteString.toString)
      | printfn %s
  }
実行その 4                               

 run | (fileFeeder  listFeeder) (consumer2 . consumer)
YYY 1
XXX 2
XXX 3
XXX 4
XXX 5
XXX 6
XXX 7
XXX 8
仲介者を使った実行                       

 run | listFeeder (joinI (isolate 2 consumer))
XXX 1
XXX 2
                                                  


isolate は与えられた数の個数だけ入力を取り出す
Iteratee 一覧


• fold
• length
• peek
• head
• drop
• take
• 他にもいくつか
Enumerator 一覧


• enumerate
• enumeratePure1Chunk
• enumeratePureNChunk
• connect
• repeat
• replicate
• enumList
Enumeratee 一覧



• isolate, isolateWhile, isolateUntil
• concatMap
• map
• filter
余談:Iteratee 触るまでにかかった期間とか
余談:Iteratee 触るまでにかかった期間とか


 • すごい H 本再読 1 週間
余談:Iteratee 触るまでにかかった期間とか


 • すごい H 本再読 1 週間
 • Haskell の enumerator コードリーディング 3
  日?
余談:Iteratee 触るまでにかかった期間とか


 • すごい H 本再読 1 週間
 • Haskell の enumerator コードリーディング 3
   日?
 • scalaz コードリーディング挫折 2 日
余談:Iteratee 触るまでにかかった期間とか


 • すごい H 本再読 1 週間
 • Haskell の enumerator コードリーディング 3
   日?
 • scalaz コードリーディング挫折 2 日
 • FSharpx.Iteratee のバグ潰し 1 日
余談:Iteratee 触るまでにかかった期間とか


 • すごい H 本再読 1 週間
 • Haskell の enumerator コードリーディング 3
   日?
 • scalaz コードリーディング挫折 2 日
 • FSharpx.Iteratee のバグ潰し 1 日
 • FSharpx.Iteratee 追加実装 1 週間
まとめとか



• Iteratee とは
• FSharpx 内での実装
• Haskell や Scala では使われているらしい
• FSharpx は F#の勉強になるよ!
ご清聴ありがとう
 ございました

FP習熟度レベルとFSharpxのIteratee