29

在 F# monad 中,如果您说let!,编译器会将其转换为Bind您在 monad 构建器上定义的成员。

现在我看到有 Query monad,如MSDN 上所示,您可以在其中说:

query {
    for student in db.Student do
    select student
    count
}

selectand count,例如,将被翻译成QueryBuilder成员Linq.QueryBuilder.SelectLinq.QueryBuilder.Count

我的问题是,这种关键字到成员的映射是硬连线到 F# 编译器中的,还是可扩展的?例如,我可以这样说:

FooMonadBuilder() {
    bar
}

并以某种方式告诉bar映射到FooMonadBuilder.Bar()方法的 F# 编译器?

4

2 回答 2

42

在 F# 2.0(即 Visual Studio 2010)中,没有办法扩展关键字列表(Ramon 的扩展除外)。但是,F# 3.0 (Visual Sutdio 11) 中的查询机制是可扩展的,您可以定义自己的关键字,类似于selectand count

这是一个基本示例,它使用关键字定义了诸如seqbuilder之类的东西:reverse

type SeqBuilder() =
    // Standard definition for 'for' and 'yield' in sequences
    member x.For (source : seq<'T>, body : 'T -> seq<'R>) =
      seq { for v in source do yield! body v }
    member x.Yield item =
      seq { yield item }

    // Define an operation 'select' that performs projection
    [<CustomOperation("select")>]
    member x.Select (source : seq<'T>, [<ProjectionParameter>] f: 'T -> 'R) : seq<'R> =
        Seq.map f source

    // Defines an operation 'reverse' that reverses the sequence    
    [<CustomOperation("reverse", MaintainsVariableSpace = true)>]
    member x.Expand (source : seq<'T>) =
        List.ofSeq source |> List.rev

let mseq = SeqBuilder()

尚未记录其工作原理的详细信息,但该CustomOperation属性表示该操作应被视为特殊语法(您可以设置各种属性以指定其行为方式 -MaintainsVariableSpace意味着它不会更改序列内的值)。该Projectionparameter属性指定关键字后面的表达式应隐式转换为函数。

现在,mseq构建器同时支持selectreverse

let q = mseq { for i in 1 .. 10 do
               select (i + 100)
               reverse }
于 2012-02-14T10:28:42.837 回答
4

简短的回答:没有。

我已经扩展了编译器以支持它,欢迎您阅读我的博客文章http://ramon.org.il/wp/2011/04/taking-computation-expressions-one-step-further/

于 2012-02-14T07:05:47.193 回答