如何在计算表达式中定义自定义运算符

Posted

技术标签:

【中文标题】如何在计算表达式中定义自定义运算符【英文标题】:How to define custom operator in computational expression 【发布时间】:2016-10-10 19:48:06 【问题描述】:

我想在我的计算表达式上定义一些自定义运算符,但无法使其工作

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) ... 会出现同样的错误。

【问题讨论】:

我认为这不可能。不过,这会很酷。 您应该能够通过单独定义这些运算符(即不作为计算构建器的一部分)来实现此语法,并使它们返回一个monad实例,然后可以是let!-ed或return!-ed. 您能提出任何替代方案吗? @baio 请尽量减少对 cme​​ts 的编辑。在您编辑了上面的评论后,我的回复不再有意义。 在实践中这种情况很少发生。当它打开时,消费者可以控制他们打开哪些模块,哪些不打开,如果他们真的需要同时使用这两个操作符,他们可以轻松地重命名一些操作符。但你是对的:实际上,运营商很少是一个好主意。您应该避免创建自己的运算符,当您确定要这样做时,应该将它们放在单独的模块中。 【参考方案1】:

正如 cmets 中已经提到的,计算表达式和自定义运算符是两个正交的语言特性,它们不会以任何方式交互。如果你想使用自定义运算符,你可以定义自定义运算符并使用它(你可以将它们定义为类型的成员以限制其范围,或者作为必须显式打开的模块的成员)。

如果您有兴趣将计算表达式用于应用编程风格之类的东西,值得注意的是,您可以在计算表达式中定义“类似 zip”的操作。这使您可以使用良好的语法编写压缩:

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(和其他)的自定义操作。)

【讨论】:

以上是关于如何在计算表达式中定义自定义运算符的主要内容,如果未能解决你的问题,请参考以下文章

delphi 自定义公式

自定义函数错误:“表达式不能在计算列中使用”

如何使用反射调用自定义运算符

如何定义自定义跨平台 size_t 类型?

在自定义列中编辑简单运算公式(Power Query 之 M 语言)

JS自定义对象,正则表达式,JQuery中的一些知识点