読者です 読者をやめる 読者になる 読者になる
ようこそ。睡眠不足なプログラマのチラ裏です。

F#でもdynamic(動的型付け)したかったけど・・・、The ? Operatorで我慢の子。

F# C#4.0 VB10

C#4.0およびVB10で利用できるようになった、System.Dynamic.ExpandoObjectクラス。
これはDLR(動的言語ランタイム)の相互運用モデルのひとつとして機能する。
例えば、ExpandoObjectクラスのインスタンスを、IronPythonの関数に渡すことができる。
このSystem.Dynamic.ExpandoObjectクラスは、いろいろと遊べて面白そうなので、
F#でも使いたくて、いろいろがんばって悪戦苦闘していたのですが、その甲斐虚しく・・・。
単なる私のスキル不足なのかもしれませんが、あえなく詰みました(無念)


で、dynamic(動的型付け)ではないのですが、いげ太さんのブログの記事にこういうのがありました。

いげ太のブログ - The ? Operator
http://igeta.cocolog-nifty.com/blog/2009/05/question.html

let inline (?) (x: 'a) (prop: string) : 'b =
    let propInfo = typeof<'a>.GetProperty(prop)
    propInfo.GetValue(x, null) :?> 'b

let inline (?<-) (x: 'a) (prop: string) (value: 'b) =
    let propInfo = typeof<'a>.GetProperty(prop)
    propInfo.SetValue(x, value, null)

let getText x : string =
    x?Text

let setText (s: string) x =
    x?Text <- s; x

type Foo() =
    let mutable text = ""
    member x.Text with get() = text and set(s) = text <- s

let _ =
    let foo = new Foo()
    foo |> setText "FooBar" |> getText |> printfn "%s"
    stdout.Write("press enter key to exit..."); stdin.ReadLine()


これは、System.Refrectionを利用する演算子(?)および(?<-)を定義することで、
あたかもオブジェクトに対して動的型付けしてプロパティにアクセスしているかのように
記述することができるようになっていますね。これは面白いだけではなく、実用性もありそうです。


ということで、せっかくなのでMethodのInvoke版も書いてみました。

open System

let inline (?) (x: 'a) (prop: string) : 'b =
    let propInfo = typeof<'a>.GetProperty(prop)
    propInfo.GetValue(x, null) :?> 'b

(* 
共存できない子
let inline (?<-) (x: 'a) (prop: string) (value: 'b) =
  let propInfo = typeof<'a>.GetProperty(prop)
  propInfo.SetValue(x, value, null)
*)

let inline (?<-) (x: 'a) (m : string) (args : obj[]) : 'b =
  let methodInfo = typeof<'a>.GetMethod(m)
  methodInfo.Invoke(x,args) :?> 'b

let Invoke args x = 
    (x?Owata <- args) |> printfn "%s"

type Hoge () =
    let mutable text = ""
    member this.Owata s1 s2 = s1 + "\(^o^)/" + s2 |> printfn "%s"
                              "/(^o^)\ナンテコッタイ"
let _ =
    let hoge = new Hoge()
    hoge |> Invoke [|"人生"; "オワタ"|] |> ignore
    stdin.ReadLine()


実行結果

人生\(^o^)/オワタ
/(^o^)\ナンテコッタイ


意図通りに動いていますね。やったね!


ですが、同一演算子のオーバーロードができず、
Propertyアクセス版の(?<-)と共存させることができませんでした。
うまく共存させる方法ないのかなあ。