如何在计算表达式中定义自定义运算符
How to define custom operator in computational expression
我想在我的计算表达式上定义一些自定义运算符,但无法实现
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) ...
会产生同样的错误。
正如评论中已经提到的,计算表达式和自定义运算符是两个不以任何方式交互的正交语言功能。如果你想使用自定义运算符,你可以定义自定义运算符并使用它(你可以将它们定义为类型的成员以限制它们的范围,或者定义为必须显式打开的模块的成员)。
如果您有兴趣将计算表达式用于类似应用程序编程风格的东西,值得注意的是您可以在计算表达式中定义"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 中有大量示例展示了如何定义类似 zip 的(和其他)自定义操作。)
我想在我的计算表达式上定义一些自定义运算符,但无法实现
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) ...
会产生同样的错误。
正如评论中已经提到的,计算表达式和自定义运算符是两个不以任何方式交互的正交语言功能。如果你想使用自定义运算符,你可以定义自定义运算符并使用它(你可以将它们定义为类型的成员以限制它们的范围,或者定义为必须显式打开的模块的成员)。
如果您有兴趣将计算表达式用于类似应用程序编程风格的东西,值得注意的是您可以在计算表达式中定义"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 中有大量示例展示了如何定义类似 zip 的(和其他)自定义操作。)