F#: есть ли способ расширить список ключевых слов монад?

Внутри монады F#, если вы скажете let!, компилятор преобразует это в член Bind, который вы определили в построителе монады.

Теперь я вижу монады Query, как показано здесь, в MSDN, где вы можете сказать:

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

а select и count, например, будут переведены в QueryBuilder участники Linq.QueryBuilder.Select и Linq.QueryBuilder.Count.

Мой вопрос в том, является ли это сопоставление ключевых слов с членами жестко зашитым в компилятор F# или оно расширяемое? Например, могу ли я сказать что-то вроде:

FooMonadBuilder() {
    bar
}

и как-то сообщить компилятору F #, что bar сопоставляется с методом FooMonadBuilder.Bar()?


person FSharpN00b    schedule 14.02.2012    source источник


Ответы (2)


В F# 2.0 (то есть Visual Studio 2010) нет возможности расширить список ключевых слов (кроме расширения Ramon). Однако механизм запросов в F# 3.0 (Visual Sutdio 11) является расширяемым, и вы можете определить свои собственные ключевые слова, подобные select и count.

Вот базовый пример, который определяет что-то вроде seq builder с ключевым словом 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 поддерживает как select, так и reverse:

let q = mseq { for i in 1 .. 10 do
               select (i + 100)
               reverse }
person Tomas Petricek    schedule 14.02.2012
comment
Не могли бы вы определить joinads с этими расширениями? Я не думаю, что вы можете; просто любопытно. - person ; 15.02.2012
comment
Фантастический. С нетерпением жду возможности прочитать все об этом. Также с нетерпением жду возможности поэкспериментировать с этим атрибутом CustomOperation в монаде, не связанной с запросами, и посмотреть, что получится. :) - person FSharpN00b; 15.02.2012
comment
Первое, что я собираюсь сделать, это создать AI DSL для нового прототипа видеоигры. - person gradbot; 20.03.2012
comment
@gradbot Звучит круто :-) Я бы хотел на это посмотреть. Я полагаю, что было бы неплохо также кодировать задачи Solver Foundation. - person Tomas Petricek; 20.03.2012
comment
@TomasPetricek Действительно ли требуется атрибут MaintenancesVariableSpace, поскольку по умолчанию «источник» неизменяем? Также я попробовал ваш код без атрибута, и результат такой же, как с атрибутом, так и без него. - person Bikal Lem; 19.01.2014

Краткий ответ: нет.

Я расширил компилятор для поддержки этого, вы можете прочитать мою статью в блоге http://ramon.org.il/wp/2011/04/taking-computation-expressions-one-step-далее/

person Ramon Snir    schedule 14.02.2012
comment
Спасибо Рамон. Если это недоступно на языке, я сомневаюсь, что в конечном итоге сделаю это, но я с нетерпением жду возможности прочитать, как вы это сделали. - person FSharpN00b; 14.02.2012