2016-10-10 20 views
2
私は私の計算式にいくつかのカスタム演算子を定義したい

でカスタム演算子を定義するために、それはmember this.Map (f, s) ...member this.Map f s ...を変更ところで計算式

type ZipSeq() = 

    [<CustomOperation("<*>")>] 
    member this.Apply f s = 
     f |> Seq.zip s |> Seq.map (fun (y, x) -> x(y)) 

    member this.Return x = 
     Seq.initInfinite (fun _ -> x) 

    // (a -> b) -> seq<a> -> seq<b> 
    [<CustomOperation("<!>")>] 
    member this.Map f s = 
     this.Apply (this.Return f) s 

let zipSeq = new ZipSeq() 

let f (a : float) = a * a 
let s = seq { yield 1. } 

// seq<b> 
let h1 = zipSeq.Map f s 

//thinking h1 should be the same as h2 
//but compilation error : ` This value is not a function and cannot be applied` 
let h2 = zipSeq { return f <!> s } 

を動作させることはできませんどのように同じエラーを与えます。

+1

私はそれが可能ではないと思います。しかし、それはクールだ。 – Tarmil

+0

これらの演算子を別々に定義するだけで(つまり計算ビルダの一部ではなく)、この構文を実現し、モナドのインスタンスを返すようにする必要があります。これは 'let!' -edまたは 'return! ed。 –

+0

他に何か提案してもらえますか? – baio

答えて

4

コメントで既に述べたように、計算式とカスタム演算子は、決して相互作用しない2つの直交言語の機能です。カスタム演算子を使用する場合は、カスタム演算子を定義して使用するだけです(範囲を制限するタイプのメンバーとして、または明示的に開く必要のあるモジュールのメンバーとして定義することができます)。

アプリケーションプログラミングスタイルのようなもので計算式を使用することに興味がある場合は、が計算式で「ジップ式」演算を定義することに注目する価値があります。これは、あなたが素敵な構文でビュン書き込むことができます:

zipSeq { 
    for x in [1; 2; 3] do 
    zip y in ['a'; 'b'; 'c'] 
    yield x, y } 

これは[1,a; 2,b; 3,c]とのシーケンスを生成します。次のようにあなたがこれを行うことができます 計算ビルダーの定義はなります

type SeqZipBuilder() = 
    member x.For(ev:seq<'T>, loop:('T -> #seq<'U>)) : seq<'U> = 
     Seq.collect loop ev 
    member x.Yield(v:'T) : seq<'T> = seq [v] 
    [<CustomOperation("zip",IsLikeZip=true)>] 
    member x.Zip 
     (outerSource:seq<'Outer>, innerSource:seq<'Inner>, 
     resultSelector:('Outer -> 'Inner -> 'Result)) : seq<'Result> = 
     Seq.map2 resultSelector outerSource innerSource 

let zipSeq = SeqZipBuilder() 

は(私の知る限りでは、これは非常によく文書化されていませんが、どのようにジッパーのような表示さF# repo testsの例の束があります(およびその他の)カスタム操作を定義することができます。