7. パイプライン演算子 |> h (g (f x)) ↓ x |> f |> g |> h ネストいらずって 素敵やん!? 関数適用のネストをなくして見やすくする演算子。補助的な演算子。 前から順番に、xをfに適用して、その返り値をgに適用して、また その返り値をhに適用して、その結果を得る。わかりやすい!
8. パイプライン演算子? let (|>) x f = f x let wup n = 2 * n let ans =2 |> wup |> wup |> wup // valans : int = 16 (|>)は、前向きなパイプライン演算子、前方パイプ演算子。 定義はこんな風になっている。ちょっとした例も。ダブルアップで倍々ゲーム。 でもさ、若干だけど、重くなるんじゃない? 無駄じゃない、こんなの?
9. パイプライン演算子! let inline (|>) x f = f x let wup n = 2 * n let ans = 2 |> wup |> wup |> wup // let ans = wup (wup (wup 2)) // valans : int = 16 (|>)は、実際にはインライン関数(演算子)として定義されています。 インライン展開されるので実行時のコストはゼロ。 気兼ねなく、存分に使ってください。乱用バッチコイ。
10. 後ろから前から h @@ g @@ f @@ x h (g (f x)) x |> f |> g |> h 関数型の 語順 でもさ、関数型コードとの親和性で言えば@@の方がいいんでない?
15. F#「OCamlと違うんです」 (* OCaml *) let n = 2 + 3 (* 5 *) let m = 2.0 +. 3.0 (* 5.0 *) // F# let n = 2 + 3 // 5 let m = 2.0 + 3.0 // 5.0 たとえば、.NETには演算子オーバーロードがある。 型推論の完全性を重んじるOCamlとは一線を画す。
16. オーバーロードと型推論 let func1 x = System.Console.WriteLine(x)// error FS0041 x + 1 let func2 (x: int) = System.Console.WriteLine(x) x + 1 let func3 x = let y = x + 1 System.Console.WriteLine(x) y let func4 x = printfn "%A" x x + 1 まあ割と型推論と相性が悪いのですけどもー。 型推論ってば、評価時の型環境すべてをひっくるめて見てくれる。 でも、オーバーロード解決は、その場その場で行われる。
18. デモ > String.length "hoge";; val it : int = 4 > "hoge".Length;; val it : int = 4 > String.length;; val it : (string -> int) = <fun:it@3> > (fun s -> s.Length);; (fun s -> s.Length);; ----------^^^^^^^^ stdin(4,11): error FS0072: このプログラムの場所の前方にある情報に基づく不確定の型のオブジェクトに対する参照です。場合によっては、オブジェクトの型を制約する型の注釈がこのプログラムの場所の前に必要です。この操作で参照が解決される可能性があります。 > (fun (s: string) -> s.Length);; val it : string -> int = <fun:clo@5> とりあえず、OCaml的に考えて、どんなふうに残念かってーのをデモで見ます。 ドット記法で書かれたオブジェクトの型は推論されない! 型注釈に頼らざるを得ない。
19. デモ > let intLength n = - (fun (s: string) -> s.Length) - ( (fun n -> n.ToString()) n );; valintLength : 'a -> int > let intLength n = - (fun (s: string) -> s.Length) - ( (fun (n: int) -> n.ToString()) n );; valintLength : int -> int > let intLength (n: int) = - n |> (fun n -> n.ToString()) |> (fun s -> s.Length);; valintLength : int -> int ちょっと意図的な例。それぞれの引数に型注釈付けるのは煩雑極まりない。 なんとかならない? それまでに型が決まればいい。F#の型決定は左から右。 なので、前方パイプ演算子を使えば、残念さをすこしはカバーできるかも!?