我正在尝试应用F# 中描述的免费 monad 模式来实现数据访问(用于 Microsoft Azure 表存储)
例子
假设我们有三个数据库表和三个 dao 的 Foo、Bar、Baz:
Foo Bar Baz
key | col key | col key | col
--------- --------- ---------
foo | 1 bar | 2 |
我想选择带有 key="foo" 的 Foo 和带有 key="bar" 的 Bar 来插入带有 key="baz" 和 col=3 的 Baz
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
导致一个函数调用,它接受 akey : 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 的定义以在整个计算过程中使用不同的类型?