История изменений
Исправление
dave,
(текущая версия)
:
b - это построитель. В интернете ты, наверное, уже видел один пример построителя: это async. Построитель и определяет то, как задается вычисление.
Что касается While, то его можно определить для любой монады. В общем случае ему нужны лишь Bind и Zero (для монады Zero обычно = return (), а Bind есть монадическая связка):
let rec whileC p m =
if p () then
bindC m (fun () -> whileC p m)
else
zeroC
Здесь b.Bind тождественно bindC, b.Zero = zeroC, а b.While = whileC. Просто сначала проще определить функции, а потом - построитель, который является объектом (да, ООП).
Это общее для монад определение while (но не для моноидов!). Точно такое же определение используется в плагине продолжений Scala. Но для многих монад while можно определить часто гораздо эффективнее, и F# это позволяет.
Вместо нотации do в F# берут произвольный построитель и ставят за ним скобки:
b {
...
}
Код внутри скобок преобразуется в заданное построителем вычисление. Как я уже писал, такой код мало отличается от обычного кода F#, и в этом главный смысл. while внутри этого кода будет преобразован, как я показал выше.
Если более точно, то тот код получается после:
b {
while expr do cexpr
}
P.S. На самом деле, там получится что-то вроде
b.Delay (fun () -> b.While ((fun () -> expr), b.Delay (fun () -> "cexpr")))
или даже
b.Run (b.Delay (fun () -> b.While ((fun () -> expr), b.Delay (fun () -> "cexpr"))))
если у построителя определен метод Run, но это детали.
P.P.S. Можно узнать точно, что получилось. Для этого надо использовать «квази-цитирование», и еще надо уметь читать его выхлоп.
Исправление
dave,
:
b - это построитель. В интернете ты, наверное, уже видел один пример построителя: это async. Построитель и определяет то, как задается вычисление.
Что касается While, то его можно определить для любой монады. В общем случае ему нужны лишь Bind и Zero (для монады Zero обычно = return (), а Bind есть монадическая связка):
let rec whileC p m =
if p () then
bindC m (fun () -> whileC p m)
else
zeroC
Здесь b.Bind тождественно bindC, b.Zero = zeroC, а b.While = whileC. Просто сначала проще определить функции, а потом - построитель, который является объектом (да, ООП).
Это общее для монад определение while (но не для моноидов!). Точно такое же определение используется в плагине продолжений Scala. Но для многих монад while можно определить часто гораздо эффективнее, и F# это позволяет.
Вместо нотации do в F# берут произвольный построитель и ставят за ним скобки:
b {
...
}
Код внутри скобок преобразуется в заданное построителем вычисление. Как я уже писал, такой код мало отличается от обычного кода F#, и в этом главный смысл. while внутри этого кода будет преобразован, как я показал выше.
Если более точно, то тот код получается после:
b {
while expr do cexpr
}
P.S. На самом деле, там получится что-то вроде
b.Delay (fun () -> b.While ((fun () -> expr), b.Delay (fun () -> "cexpr")))
или даже
b.Run (b.Delay (fun () -> b.While ((fun () -> expr), b.Delay (fun () -> "cexpr"))))
если у построителя определен метод Run, но это детали.
Исходная версия
dave,
:
b - это построитель. В интернете ты, наверное, уже видел один пример построителя: это async. Построитель и определяет то, как задается вычисление.
Что касается While, то его можно определить для любой монады. В общем случае ему нужны лишь Bind и Zero (для монады Zero обычно = return (), а Bind есть монадическая связка):
let rec whileC p m =
if p () then
bindC m (fun () -> whileC p m)
else
zeroC
Здесь b.Bind тождественно bindC, b.Zero = zeroC, а b.While = whileC. Просто сначала проще определить функции, а потом - построитель, который является объектом (да, ООП).
Это общее для монад определение while (но не для моноидов!). Точно такое же определение используется в плагине продолжений Scala. Но для многих монад while можно определить часто гораздо эффективнее, и F# это позволяет.
Вместо нотации do в F# берут произвольный построитель и ставят за ним скобки:
b {
...
}
Код внутри скобок преобразуется в заданное построителем вычисление. Как я уже писал, такой код мало отличается от обычного кода F#, и в этом главный смысл. while внутри этого кода будет преобразован, как я показал выше.
Если более точно, то тот код получается после:
b {
while expr do cexpr
}