F # Вычисления Выражения: как использовать `for` для возврата` seq`?
Я пишу выражение вычисления, которое по существу реализует State monad, и я пытаюсь использовать выражение for
.
Я могу использовать функцию шаблона forLoop
или даже MBuilder.For()
, и все они возвращают приятный M<seq<'U>, _>
, который может быть обработан дальнейшим выражением let!
. Но когда я пытаюсь сделать то же самое с выражением for
, он не может скомпилировать, говоря, что выражение внутри for
должно возвращать unit
.
Прошу прощения за большой блок кода, который я не могу сделать меньше.
type M<'T, 'E> = 'T * 'E // Monadic type is a simple tuple
type MFunc<'T, 'U, 'E> = 'T -> M<'U, 'E> // A function producing monadic value
// typical boilerplate functions
let bind (x: M<'T, 'E>) (f: MFunc<'T, 'U, 'E>) : M<'U, 'E> =
let a, s = x
let b, s1 = f a
b, s1 + s
let combine (e1: M<'T, 'E>) (e2: M<'U, 'E>) : M<'U, 'E> = bind e1 (fun _ -> e2)
let delay f = (fun () -> f())()
// These two are explained below
let combineList (e1: M<'T, 'E>) (e2: M<'T seq, 'E>) : M<'T seq, 'E> =
bind
e1
(fun x1 ->
let e2body, e2state = e2
seq{yield! e2body; yield x1}, e2state
)
let forLoop (xs: seq<'T>) (f: MFunc<'T, 'U, 'E>) : M<seq<'U>, 'E> =
Seq.fold
(fun s x -> combineList (f x) s)
(Seq.empty<'U>, 0)
xs
// Builder class
type MBuilder() =
member this.Bind (x: M<'T, 'E>, f: MFunc<'T, 'U, 'E>) : M<'U, 'E> = bind x f
member this.Return(a) = a, 0
member this.Combine(e1,e2) = combine e1 e2
member this.Delay(f) = delay f
member this.Zero() = (), 0
member this.For (xs: seq<'T>, f: MFunc<'T, 'U, 'E> ) : M<seq<'U>, 'E> = forLoop xs f
let stateful = new MBuilder()
let mTest = stateful {
// below is the typical use, just for example
let! var1 = "q", 3
let! var2 = true, 4
// so far so good, the monad returns ("test", 7)
return "test"
}
Теперь я пытаюсь использовать петли. Следующие три вызова работают должным образом, увеличивая состояние столько раз, сколько есть элементов в myList
. Они также возвращают красивый string seq
, очевидно, за исключением последнего вызова:
let myList = ["one"; "two"; "three"] // define test data
let! var3 = stateful.For(myList, (fun x -> x, 1))
let! var4 = forLoop myList (fun x -> x, 1)
// No return value, as expected
for str in myList do
let! _ = str, 1
return ""
Но следующее не компилируется: error FS0001: This expression was expected to have type M<'a,int> but here has type unit
let! var5 =
for str in myList do
let! _ = str, 1
return ""
Итак, мой вопрос - Что я делаю неправильно?
Я также немного смущен двумя перегрузками for
описанных здесь и как использовать оба.
Ответы
Ответ 1
Код, который вы пытаетесь написать, не является синтаксически правильным выражением вычисления. Синтаксис не позволяет создавать конструкции выражения вычислений в выражении e
в let! v = e
.
Если вы хотите использовать вложенное выражение вычисления, вам нужно написать:
let mtest = stateful {
let! var5 =
stateful { for str in myList do
let! _ = str, 1
return "" }
return "something here" }
Это должно ответить на ваш ближайший вопрос, но есть ряд вещей, которые я нахожу довольно запутанными в ваших определениях:
-
Тип вашего For
запутан. Он должен быть либо seq<'T> -> ('T -> M<'R>) -> M<'R>
(если ваша монада может комбинировать несколько результатов) или seq<'T> -> ('T -> M<unit>) -> M<unit>
(если ваше вычисление возвращает только одно значение)
-
Вы иногда используете seq<'T>
внутри M<_>
в результате (в For
), но иногда ваша монада возвращает только одно значение. Вы должны использовать тот же самый монадический тип везде.
-
Конструкция For
может быть определена в терминах Zero
и Combine
. Если вы не делаете что-то особенное, это лучший способ пойти. См. Пример в спецификации F #.
Если вы хотите получить более подробный документ, посмотрите в этой статье, в котором описываются различные варианты.