Я пытаюсь применить свободный образец монады, как описан в F# for fun and profit для осуществления доступа к данным (для Microsoft Azure Table Storage)Free Монада в F # с общими выходными типа
Примера
Давайте предположим, что мы имеем три таблицы базы данных и три Dao в Foo, Bar, баз:
Foo Bar Baz
key | col key | col key | col
--------- --------- ---------
foo | 1 bar | 2 |
Я хочу, чтобы выбрать Foo с ключом = «Foo» и бар с ключом = «бар», чтобы вставить баз с ключом = «бАЗ» и цв = 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
Pro я проблема
Я определил две операции 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'. Если вы посмотрите на это внимательно, вы увидите, что он может только выражать цепочки операций одного типа. –
Не удалось бы избежать всех этих уровней косвенности и сделать простой материал CRUD в чем-то вроде [сценария транзакций] (https://martinfowler.com/eaaCatalog/transactionScript.html)? Это похоже на то, что Томас Петричек описывает в последнем абзаце своего [ответа] (http://stackoverflow.com/a/41668459/467754). См. Также [Почему бесплатная Монада не является бесплатной] (https://www.youtube.com/watch?v=U0lK0hnbc4U). –
Текущая реализация - это простой набор императивных функций CRUD. Пожалуйста, см. Комментарий ниже для мотивации. – dtornow