F#: 是否有一种方法扩展Monad关键字列表?

29
在 F# monad 中,如果您使用 let!,编译器会将其转换为您在 monad builder 上定义的 Bind 成员。
现在我看到有查询 monads,如 在 MSDN 上显示,您可以这样说:
query {
    for student in db.Student do
    select student
    count
}

例如,selectcount 将被翻译为 QueryBuilder 成员 Linq.QueryBuilder.SelectLinq.QueryBuilder.Count

我的问题是,这些关键字到成员的映射是硬编码到 F# 编译器中的,还是可扩展的?例如,我能否像下面这样说:

FooMonadBuilder() {
    bar
}

并且要以某种方式告诉F#编译器,bar对应于FooMonadBuilder.Bar()方法?

2个回答

43
在F# 2.0(即Visual Studio 2010)中,除了Ramon的扩展之外,没有扩展关键字列表的方法。然而,在F# 3.0(Visual Studio 11)中的查询机制是可扩展的,您可以定义自己的关键字,类似于selectcount
这是一个基本示例,定义了类似于seq构建器的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 }

你能用这些扩展定义joinads吗?我不认为你能,只是好奇。 - user29439
太棒了。期待阅读所有关于它的内容。也期待在非查询相关的单子中尝试使用CustomOperation属性,看看会发生什么。 :) - FSharpN00b
1
我要做的第一件事是为新的视频游戏原型制作一个AI DSL。 - gradbot
1
@gradbot 听起来很棒 :-) 我很想看看。我想它也适用于编码 Solver Foundation 问题。 - Tomas Petricek
@TomasPetricek 是否真的需要MaintainsVariableSpace属性,因为默认情况下“source”是不可变的?此外,我尝试了您的代码,没有使用该属性,结果与使用该属性的结果相同。 - Bikal Lem

4

谢谢你,Ramon。如果这种语言不可用,我就不会去做了,但我很期待阅读你是如何做到的。 - FSharpN00b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接