moonmile solutions 分室

ソフトウェア開発者の情報収集をリアルタイムで垂れ流し...という具合に行きます。

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 の違いを回避できるっていう裏技?と同じ。