Я пытаюсь узнать немного больше о вычислительных выражениях F #, реализуя одно из моих собственных. Однако я наткнулся на камень преткновения в отношении метода Bind
. Вот что у меня есть на данный момент:
type public op<'a> = Op of ('a list -> 'a list)
let inline (>>) (Op a) (Op b) = Op (a >> b)
module Op =
let id = Op id
let bind (b : 'b -> op<'a>) (v : 'b) = b v
let call (Op f) = f
let push v = Op (fun t -> v :: t)
// .. snip ..
type OpBuilder() =
member __.Bind (v, b) = Op.bind b v
member __.Yield (()) = Op.id
member __.Return (m) = Op.call m
[<CustomOperation("push")>]
member __.Push (m : op<'a>, v : 'a) = m >> Op.push v
// .. snip ..
let op = new OpBuilder()
Насколько я понимаю, все следующее должно быть примерно эквивалентно:
// Use only the Op module methods
let f1 = Op.call(Op.bind (fun x -> Op.push x) 1)
// Use op builder with external closure
let f2 = Op.call(let x = 2 in op { push x })
// Use op builder bind explicitly
let f3 = op.Return(op.Bind(3, fun x -> op.Push(op.Yield(), x)))
// Use op builder with let! binding
let f4 = op { let! x = 4 in push x }
Первые 3 вроде работают нормально, однако f4
выдает эту ошибку:
Ожидалось, что это выражение будет иметь тип
unit
, но здесь есть тип
int
Я получаю ту же ошибку, если использую let
вместо let!
. Все, что я прочитал, предполагает, что это правильный способ реализации Bind
, но, видимо, я чего-то упускаю. Кто-нибудь может указать, что я делаю не так?
op { push 2; dup; add } []
→[4]
. - person p.s.w.g   schedule 16.07.2016bind
, - это не привязка, а просто приложение-функция. Чтобы сделать это монадическим связыванием, второй параметр должен бытьop<'b>
, а не'b
. - person Fyodor Soikin   schedule 16.07.2016let swap = op { let! x = pop; let! y = pop; push x; push y }
, и я решил, что начать с постоянных значений (let! x = 4
) будет проще.pop
- это разновидностьop<'a>
, поскольку он изменяет стек, но я понятия не имел, как получить всплывающее значение в качестве переменной. - person p.s.w.g   schedule 16.07.2016pop
- поскольку ее тип будет соответствовать'a list -> 'a * 'a list
, а одинокий'a
- это всплывающее значение. Я могу дать вам такой ответ, но не хочу портить вам удовольствие. - person scrwtp   schedule 16.07.2016Op.pop : ('a -> op<'a>) -> op<'a>
(например,pop (fun x -> pop (fun y -> push x >> push y))
, но я не уверен, как перевести это наOpBuilder
. Я попробую ваше предложение. - person p.s.w.g   schedule 16.07.2016ILBuilder
, но все еще трудно понять. Есть ли у вас какая-либо другая документация о логике и обосновании того, как вы ее спроектировали? - person p.s.w.g   schedule 16.07.2016'a0
,'a1
, ...,'an
, как общий типT<'a0 * (a1 * (... * (an * unit)))>
(где нет ничего особенного вunit
или конструкторе двоичного кортежа*
- вам просто нужно некоторый понятный способ кодирования пустого списка на уровне типа и преобразования одного элемента в другой список). Затем для каждого оператора, который вас интересует, вы даете ему подпись, которая учитывает правильную семантику (например,pop
принимаетT<'a * 'as>
наT<'as>
,dup
принимаетT<'a * 'as>
наT<'a * ('a * 'as)>
и т. Д.). - person kvb   schedule 16.07.2016type T<'x> = T of Instruction list
, где параметр типа'x
является фантомным типом, поскольку он не встречается в правой части определения. Тогда, пока ваши открытые операции имеют правильные подписи, все работает нормально. Надеюсь, что это поможет - это немного сложно, но, надеюсь, основные концепции не так уж и плохи. - person kvb   schedule 16.07.2016