2016-10-10 120 views
2

我想確定我的計算表達一些運營商定製定義自定義操作,但不能使它工作如何計算表達式

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 } 

順便說一句,改變member this.Map f s ...member this.Map (f, s) ...給人同樣的錯誤。

+1

我不認爲這是可能的。不過,這很酷。 – Tarmil

+0

您應該能夠通過單獨定義這些運算符(即,不作爲計算生成器的一部分)來實現這種語法,並使它們返回一個monad實例,然後它可以是'let!'-ed或'return!' - 編輯。 –

+0

你能建議任何替代方案嗎? – baio

回答

4

正如在評論中已經提到的那樣,計算表達式和自定義運算符是兩個不以任何方式進行交互的正交語言特徵。如果你想使用自定義操作符,你可以定義自定義操作符並使用它(你可以將它們定義爲一個類型的成員來限制它們的範圍,或者作爲一個必須明確打開的模塊的成員)。

如果您有興趣使用類似於應用編程風格的計算表達式,值得注意的是可以在計算表達式中定義「zip-like」操作。這可以讓你寫一個不錯的語法荏苒:

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一堆例子來演示如何拉鍊樣(和其他)定製操作可以定義。)