2016-10-10 122 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一堆例子来演示如何拉链样(和其他)定制操作可以定义。)