Я пытаюсь применить шаблон бесплатной монады, как описано в F # для развлечения и прибыли для реализации доступа к данным (для хранилища таблиц Microsoft Azure)
Пример
Предположим, у нас есть три таблицы базы данных и три dao's Foo, Bar, Baz:
Foo Bar Baz
key | col key | col key | col
--------- --------- ---------
foo | 1 bar | 2 |
Я хочу выбрать Foo с key = "foo" и Bar с key = "bar", чтобы вставить Baz с key = "baz" и col = 3
Select<Foo> ("foo", fun foo -> Done foo)
>>= (fun foo -> Select<Bar> ("bar", fun bar -> Done bar)
>>= (fun bar -> Insert<Baz> ((Baz ("baz", foo.col + bar.col), fun () -> Done ()))))
В функции интерпретатора
Select
приводит к вызову функции, которая принимаетkey : string
и возвращаетobj
Insert
приводит к вызову функции, которая принимаетobj
и возвращаетunit
Проблема
Я определил две операции Select
и Insert
в дополнение к Done
, чтобы завершить вычисление:
type StoreOp<'T> =
| Select of string * ('T -> StoreOp<'T>)
| Insert of 'T * (unit -> StoreOp<'T>)
| Done of 'T
Чтобы связать StoreOp в цепочку, я пытаюсь реализовать правильную функцию привязки:
let rec bindOp (f : 'T1 -> StoreOp<'T2>) (op : StoreOp<'T1>) : StoreOp<'T2> =
match op with
| Select (k, next) ->
Select (k, fun v -> bindOp f (next v))
| Insert (v, next) ->
Insert (v, fun () -> bindOp f (next ()))
| Done t ->
f t
let (>>=) = bindOp
Однако компилятор f # правильно предупреждает меня, что:
The type variable 'T1 has been constrained to be type 'T2
Для этой реализации bindOp тип фиксируется на протяжении всего вычисления, поэтому вместо:
Foo > Bar > unit
все, что я могу выразить, это:
Foo > Foo > Foo
Как мне изменить определение StoreOp и / или bindOp для работы с разными типами на протяжении всего вычисления?
bindOp
коде, но основная причина - вашStoreOp
тип. Если вы посмотрите на него внимательно, вы увидите, что он может выражать цепочки операций только одного типа. - person Fyodor Soikin   schedule 16.01.2017