F# で 'a(ジェネリック/総称)を型推論して束縛する離れ業がある
おなじく pars.fs を覗いてみて分かったのだけど、
let op (x:int) = let (y:'a) = 20 let (z:'a) = "masuda" x + y
の場合には、"masuda" で bind するときにコンパイルエラーになる。
一見、'a は unit のように見えるけど、x+y の時点で
- op 関数の引数 x が int 型であること
- x + y での戻り値が int 型であること
から、'a が int 型に束縛されるので、z:'a は int 型になるんだけど、"masuda" のような文字列を入れると駄目、ってことらしい。 なるほど、これはすごい。
じゃあ、y:'a のほうを string 型に束縛すれば、コンパイルが通るだろうという推測をして
let op (x:int) = let (y:'a) = "10" let (z:'a) = "masuda" x + System.Convert.ToInt32( y )
のようにすると、これは通る。
何に使うのかよくわからんけど。
ちなみに、pars.fs には、
(fun (parseState : Internal.Utilities.Text.Parsing.IParseState) -> let _1 = (let data = parseState.GetInput(1) in (Microsoft.FSharp.Core.Operators.unbox data : 'tupleParenPatternElements)) in let _3 = (let data = parseState.GetInput(3) in (Microsoft.FSharp.Core.Operators.unbox data : 'parenPattern)) in Microsoft.FSharp.Core.Operators.box ( ( # 2509 "..\pars.fsy" _3 :: _1 ) # 2509 "..\pars.fsy" : 'tupleParenPatternElements));
ってな感じで、'tupleParenPatternElements と書かれているけど、実際は Internal.Utilities.Text.Parsing.IParseState に束縛されている、ってことで。あまり意味がない...ことはないのかな。メタ情報的に必要なのか?
ひとつ、思いついたので追記、
let (x:'a) = "なにやら良くわからない型がある" // ... let (y:'a) = "なにやらよくわからないけど、xと同じ型にしておきたい"
こんな感じで、x に bind するときに、よくわからないけど複雑な型を使う。複雑すぎて表記するのが面倒だけど、仮に「'a」という名前を付けておく。 そうしておいて、y に bind するときに、x と同じ型 'a にしたいときに使う。x と y への代入は、別々のオブジェクトだけど型は一緒にしておきたいときに使う。 更に言うと、
- 型は一緒にしておきたい。
- しかし、型を知っておく必要はない
ってのがミソ。なにやら抽象的で面白い。
~~~ もうひとつ、追記
let main = let (x:'a) = ("masuda",175) let (y:'a) = ("tomoak",001) let (z:'a) = ("other",001,"japan") 0
多分、こんな感じ。 tuple を使って 'a を string * int に束縛しておくと、z:'a の時に string * int * string を代入しようとしてコンパイルエラーになる。 C# の場合 LINQ の戻り値を var で受けたときに、それを使いまわすと select の違いを回避できるっていう裏技?と同じ。