这是this question的后续问题。
我试图创建一个计算表达式构建器,它通过自定义操作累积值,同时也支持标准的F#语言构造。为了有一个简单的例子来讨论,我使用了一个构建F#列表的计算表达式。感谢kvb和Daniel先生提出的建议,但仍然遇到for
循环问题。
建设者:
type Items<'a> = Items of 'a list
type ListBuilder() =
member x.Yield(vars) = Items [], vars
member x.Run(l,_) = l
member x.Zero() = Items [], ()
member x.Delay f = f()
member x.ReturnFrom f = f
member x.Combine((Items curLeft, _), (Items curRight, vars)) =
(Items (curLeft @ curRight), vars)
member x.Bind(m: Items<'a> * 'v, f: 'v -> Items<'a> * 'o) : Items<'a> * 'o =
let (Items current, vals) = m
x.Combine(m, f vals)
member x.While(guard, body) =
if not (guard()) then
x.Zero()
else
x.Bind(body, fun () -> x.While(guard, body))
member x.TryWith(body, handler) =
try
x.ReturnFrom(body())
with e ->
handler e
member x.TryFinally(body, compensation) =
try
x.ReturnFrom(body())
finally
compensation()
member x.Using(disposable:#System.IDisposable, body) =
let body' = fun() -> body disposable
x.TryFinally(body', fun () ->
match disposable with
| null -> ()
| disp -> disp.Dispose())
member x.For(xs:seq<'a>, body) =
x.Using(xs.GetEnumerator(), fun enum ->
x.While(enum.MoveNext, x.Delay(fun () -> body enum.Current)))
[<CustomOperation("add", MaintainsVariableSpace=true)>]
member x.Add((Items current, vars), [<ProjectionParameter>] f) =
Items (current @ [f vars]), vars
[<CustomOperation("addMany", MaintainsVariableSpace=true)>]
member x.AddMany((Items current, vars), [<ProjectionParameter>] f) =
Items (current @ f vars), vars
let listBuilder = ListBuilder()
let build (Items items) = items
此版本允许我以前无法做的事情,例如:
let stuff =
listBuilder {
let x = 5 * 47
printfn "hey"
add x
addMany [x .. x + 10]
} |> build
但是,我仍然在这个问题上遇到编译器错误:
let stuff2 =
listBuilder {
for x in 1 .. 50 do
add x
} |> build
在这种情况下,IDE正在for x in
中强调x并告诉我,&#34;此表达式应该具有类型单位,但这里有类型int。&#34;
我不清楚为什么它期望循环变量属于单位类型。显然我在某个地方得到了错误的方法签名,我怀疑我不会在我应该的每个地方都经过我的累积状态,但编译错误实际上并没有帮助我缩小我出错的地方。任何建议将不胜感激。
答案 0 :(得分:3)
直接原因是您的While
函数限制了body
的类型。但是,一般情况下,您不能同时使用自定义操作,也不能在同一计算表达式中控制流操作符,因此即使您修复了签名,我也不认为您能够完全按照自己的意愿行事。