Какова цель "делать!". обозначение в F #?
Я новичок в F #, так что это простой вопрос и, возможно, дубликат, но я не мог найти ответ нигде...
Я читаю эту реализацию LOGO DSL, и я не понимаю, в чем смысл "делать!". обозначение здесь:
this.Loaded.Add (fun _ ->
async {
do! Async.Sleep 200
for cmd in theDrawing do
do! this.Execute(cmd)
} |> Async.StartImmediate
)
Вы можете помочь?
Ответы
Ответ 1
F # выражения вычислений (a.k.a. "workflows" ) используют синтаксис
builder { expression }
где expression
может содержать специальные конструкции, включая различные ключевые слова "bang", такие как let!
и do!
. Подобно LINQ в С# или VB, выражения вычисления F # представляют собой просто синтаксический сахар (который desugars в вызовы методов на builder
).
Одним из наиболее распространенных типов вычислений является async
, как описано здесь.
В этом конкретном примере используется async
вместе с Async.Sleep
, чтобы временно выйти из потока пользовательского интерфейса, чтобы дать UI возможность перерисовать, реагировать на события мыши и т.д. Этот общий метод описан более подробно здесь.
Ответ 2
Я только добавлю, что обозначение do!
не должно явно поддерживаться выражением вычисления, потому что то же самое можно записать с помощью let!
следующим образом:
do! foo() // Using do!
let! _ = foo() // Equivalent using let!
В общем случае let!
мы использовали, когда у вас есть функция, реализованная с использованием вычислений, и вы хотите называть ее из другого выражения вычисления того же типа. Это означает, что он используется для составления вычислительных выражений. Для async
эта композиция означает, что у вас есть неблокирующий асинхронный код и вызывать его из другого асинхронного рабочего процесса определенным образом, чтобы сделать асинхронный вызов.
Ключевое слово let!
позволяет вам сделать это и получить некоторое значение в качестве результата, а do!
- это ярлык, который вы можете использовать, если вычисление ничего не возвращает.
Раздел из функционального программирования реального мира, в котором обсуждаются выражения вычислений (а также выражения последовательности), доступен в виде бесплатной выборки, поэтому, если вы хотите прочитать более подробное руководство по выражениям вычисления, это может быть хорошим источником информации:
Кстати: Должна быть возможность написать примерный код в вопросе лучше, используя примитив AwaitEvent
, например:
async {
let! _ = this.Loaded |> Async.AwaitEvent
do! Async.Sleep 200
for cmd in theDrawing do
do! this.Execute(cmd) } |> Async.StartImmediate
Это означает одно и то же: сначала он ждет, пока произойдет событие Loaded
, тогда он ждет 200 мс, а затем он выполнит оставшуюся часть работы. Это ожидание является особенным (поэтому мы используем let!
/do!
, потому что он не блокирует поток при ожидании).
Ответ 3
В этом случае он запускает выражение сна асинхронно, поэтому поток может делать что-то полезное, а не блокировать.
В общем случае let!
, use!
, yield!
и do!
выполняют "специальную" обработку содержащихся вычисляющих выражений (что бы это ни было, async
в этом случае). Например. в seq { ... }
с использованием yield!
позволяет подпоследовательность быть объединенной в вывод, а не возвращаться как один объект.