ScalaやHaskellのライブラリをF# に移植するときのTips?
個人的にはF# の中での理想やりっろーんを追い求めてみたい感もある
こんなことを思っていたりすることもあるため、HaskellやScalaに面白そうなライブラリがあると聞きつけるとF# に移植できないか試したくなります。
で、いくつかTipsというかこんなことがあるよ、的なことをまとめておきます。
用語はてきとーなので表現が微妙であれば順次修正していきます。
【この記事は書きかけです】
型パラメータが型パラメータをもつ
Free Monadを例にしてみると
sealed trait Free[S[+_], +A]
や
data Free f a = Pure a | Free (f (Free f a))
などの型パラメータが型パラメータをもってるよ的なやつです。
これが出た場合、私は白旗をあげます。
再帰なlazyちゃんマジlazy
これはscala-machinesやHaskellのmachinesのrepeatedly関数を例にしましょう。
まずはScala。
def repeatedly: Machine[K, O] = { lazy val r : Machine[K, O] = this >> r r }
次にHaskell。
repeatedly (Plan m) = r where r = m (const r) Yield Await Stop
lazyだからこそできる再帰ですね。
HaskellやScalaではlazyが型に現れることはないので、これで問題なしです。
が、F#ではこう単純にはいきません。この例の場合だと、
let rec repeatedly p : Machine<'k, 'o> = let inline (>>.) p (next : Lazy<Plan<'k, 'o, 'a>>) = p >>= (fun _ -> next.Value) let rec r = lazy (p >>. r) r.Value
こうなります。
まずevaluationが発生して欲しい場所を探します。今回の場合は(>>.)演算子でした。
これを、えいやっとシャドウィングしてLazy版を書きます。
あとは何事もなかったように、自己再帰の警告を #nowarn "40" などで消すなり放置しながら残りの部分を実装します。
こうすることで、他の関数には迷惑をかけずにScalaやHaskellと同様のことが行えます。
この系統はlazy evaluation(?)の良い勉強になりますね。
type class
前者は使ったことがないのでどんな感じなのかわかりません。
後者はFSharpx内のいくつかのmoduleに方法を見れば、どんな感じなのか把握できると思います。
あ、IOモナドはあきらめてます。
おわりに
「F#で理想を追求するって、そういうことよ」