Выражения вычисления (F#)
Вычислительные выражения языка F# обеспечивают удобный синтаксис для записи вычислений, которые можно упорядочивать и комбинировать с помощью конструкций и привязок потока управления. Они могут служить для обеспечения удобного синтаксиса компонентов Monad — средства функционального программирования, которое может использоваться для управления данными, элементами управления и побочными эффектами в функциональных программах.
Встроенные рабочие процессы
Примером вычислительного выражения являются выражения последовательностей. Они представляют собой асинхронные рабочие процессы. Дополнительные сведения о выражениях последовательностей см. в разделе Последовательности. Дополнительные сведения об асинхронных рабочих процессах см. в разделе Асинхронные рабочие процессы.
Некоторые функции являются общими и для выражений последовательностей, и для асинхронных рабочих процессов и иллюстрируют базовый синтаксис для вычислительного выражения:
builder-name { expression }
Представленный выше синтаксис определяет, что данное выражение является вычислительным с типом, заданным параметром builder-name. Вычислительное выражение может быть встроенным рабочим процессом, например seq или async, либо чем-то, определенным пользователем. Параметр builder-name — это идентификатор экземпляра специального типа, называемого типом построителя. Тип построителя — это тип класса, определяющий специальные методы, управляющие способом объединения фрагментов вычислительного выражения, т. е. кодом, который управляет выполнением выражения. Иначе можно сказать, что класс построителя позволяет настраивать работу многих конструкций языка F#, таких как циклы и привязки.
В вычислительных выражениях для некоторых общих конструкций языка доступны две формы. Варианты конструкций вызываются с использованием суффикса ! (восклицательный знак) в некоторых ключевых словах, например let!, do! и т. д. Такие специальные формы заставляют некоторые функции, определенные в классе построителя, заменять обычное встроенное поведение этих операций. Эти формы подобны форме yield! ключевого слова yield, используемого в выражениях последовательностей. Дополнительные сведения см. в разделе Последовательности.
Создание нового типа вычислительного выражения
Можно определить характеристики собственных вычислительных выражений, создав класс построителя и определив для этого класса специальные методы. В классе построителя при необходимости можно определять методы, перечисленные в следующей таблице.
В следующей таблице описаны методы, которые могут использоваться в классе построителя рабочих процессов.
Метод |
Типичные сигнатуры |
Описание |
Bind |
M<'T> * ('T -> M<'U>) -> M<'U> |
Вызывается для let! и do! в вычислительных выражениях. |
Delay |
(unit -> M<'T>) -> M<'T> |
Создает оболочку вычислительного выражения в виде функции. |
Return |
'T -> M<'T> |
Вызывается для return в вычислительных выражениях. |
ReturnFrom |
M<'T> -> M<'T> |
Вызывается для return! в вычислительных выражениях. |
Run |
M<'T> -> M<'T> или M<'T> -> 'T |
Выполняет вычислительное выражение. |
Combine |
M<'T> * M<'T> -> M<'T> или M<unit> * M<'T> -> M<'T> |
Вызывается для последовательности в вычислительных выражениях. |
For |
seq<'T> * ('T -> M<'U>) -> M<'U> seq<'T> * ('T -> M<'U>) -> seq<M<'U>> |
Вызывается для выражений for...do в вычислительных выражениях. |
TryFinally |
M<'T> * (unit -> unit) -> M<'T> |
Вызывается для выражений try...finally в вычислительных выражениях. |
TryWith |
M<'T> * (exn -> M<'T>) -> M<'T> |
Вызывается для выражений try...with в вычислительных выражениях. |
Using |
'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable |
Вызывается для привязок use в вычислительных выражениях. |
While |
(unit -> bool) * M<'T> -> M<'T> |
Вызывается для выражений while...do в вычислительных выражениях. |
Yield |
M<'T> -> M<'T> |
Вызывается для выражений yield в вычислительных выражениях. |
YieldFrom |
M<'T> -> M<'T> |
Вызывается для выражений yield! в вычислительных выражениях. |
Zero |
unit -> M<'T> |
Вызывается для пустых ветвей else выражений if...then в вычислительных выражениях. |
Во многих из методов класса построителя рабочего процесса используется и возвращается конструкция M<'T>, которая обычно представляет собой отдельно определяемый тип, характеризующий вид объединяемых вычислений, например Async<'T> для асинхронных рабочих процессов и Seq<'T> для рабочих процессов последовательностей. Сигнатуры этих методов допускают их объединение и вложение друг в друга, чтобы объект рабочего потока, возвращенный одной конструкцией, можно было передать следующей конструкции. Когда компилятор анализирует вычислительное выражение, он преобразует его в ряд вызовов вложенных функций, используя методы, представленные в предыдущей таблице, и код в вычислительном выражении.
Вложенное выражение имеет следующий вид:
builder.Run(builder.Delay(fun () -> {| cexpr |}))
В приведенном выше коде вызовы методов Run и Delay опускаются, если они не определены в классе построителя вычислительных выражений. Тело вычислительного выражения, обозначенное здесь как {| cexpr |}, преобразуется в вызовы с участием методов класса построителя посредством преобразований, приведенных в следующей таблице. Вычислительное выражение {| cexpr |} определяется рекурсивно в соответствии с этими преобразованиями, где expr — выражение F#, а cexpr — вычислительное выражение.
Выражение |
Преобразование |
---|---|
{| let binding in cexpr |} |
let binding in {| cexpr |} |
{| let! pattern = expr in cexpr |} |
builder.Bind(expr, (fun pattern -> {| cexpr |})) |
{| do! expr in cexpr |} |
builder.Bind(expr1, (fun () -> {| cexpr |})) |
{| yield expr |} |
builder.Yield(expr) |
{| yield! expr |} |
builder.YieldFrom(expr) |
{| return expr |} |
builder.Return(expr) |
{| return! expr |} |
builder.ReturnFrom(expr) |
{| use pattern = expr in cexpr |} |
builder.Using(expr, (fun pattern -> {| cexpr |})) |
{| use! value = expr in cexpr |} |
builder.Bind(expr, (fun value -> builder.Using(value, (fun value -> {| cexpr |})))) |
{| if expr then cexpr0 |} |
if expr then {| cexpr0 |} else binder.Zero() |
{| if expr then cexpr0 else cexpr1 |} |
if expr then {| cexpr0 |} else {| cexpr1 |} |
{| match expr with | pattern_i -> cexpr_i |} |
match expr with | pattern_i -> {| cexpr_i |} |
{| for pattern in expr do cexpr |} |
builder.For(enumeration, (fun pattern -> {| cexpr }|)) |
{| for identifier = expr1 to expr2 do cexpr |} |
builder.For(enumeration, (fun identifier -> {| cexpr }|)) |
{| while expr do cexpr |} |
builder.While(fun () -> expr), builder.Delay({|cexpr |}) |
{| try cexpr with | pattern_i -> expr_i |} |
builder.TryWith(builder.Delay({| cexpr |}), (fun value -> match value with | pattern_i -> expr_i | exn -> reraise exn))) |
{| try cexpr finally expr |} |
builder.TryFinally(builder.Delay( {| cexpr |}), (fun () -> expr)) |
{| cexpr1; cexpr2 |} |
builder.Combine({|cexpr1 |}, {| cexpr2 |}) |
{| other-expr; cexpr |} |
expr; {| cexpr |} |
{| other-expr |} |
expr; builder.Zero() |
В предыдущей таблице other-expr соответствует выражению, не приведенному в этой таблице. Класс построителя не обязательно должен реализовывать все из методов и поддерживать все из преобразований, приведенных в предыдущей таблице. Нереализованные конструкции не будут доступны в вычислительных выражениях данного типа. Например, если в вычислительных выражениях не требуется поддерживать ключевое слово use, определение Use в классе построителя можно опустить.
В следующем примере кода показано создание и использование простого типа вычислительного выражения, который выводит на консоль некоторые данные, отражающие ход выполнения кода.
// Program.fs
open Module1
// Create the computation expression object.
let trace1 = trace {
// A normal let expression (does not call Bind).
let x = 1
// A let expression that uses the Bind method.
let! y = 2
let sum = x + y
// return executes the Return method.
return sum
}
// Execute the code. Start with the Delay method.
let result = trace1()
В следующем примере кода реализуется класс построителя для трассировки вычислительного выражения.
// Module1.fs
module Module1 =
// Functions that implement the builder methods.
let bind value1 function1 =
printfn "Binding %A." value1
function1 value1
let result value1 =
printfn "Returning result: %A" value1
fun () -> value1
let delay function1 =
fun () -> function1()
// The builder class for the "trace" workflow.
type TraceBuilder() =
member x.Bind(value1, function1) =
bind value1 function1
member x.Return(value1) = result value1
member x.Delay(function1) =
printfn "Starting traced execution."
delay function1
let trace = new TraceBuilder()
Результат выполнения примера выглядит следующим образом.
Starting traced execution.
Binding 2.
Returning result: 3
См. также
Другие ресурсы
Журнал изменений
Дата |
Журнал |
Причина |
---|---|---|
Декабрь 2010 |
Добавлены метод Run и таблица преобразований. |
Обратная связь от клиента. |
Апрель 2011 |
В таблицу методов построителя добавлены метод Yield и обновленная сигнатура для метода For. |
Исправление ошибки содержимого. |