你如何创建一个F#工作流来支持单步操作?
我想创建一个构建器,它构建的表达式在每个步骤后都会返回类似于continuation的内容 大概是这样的:你如何创建一个F#工作流来支持单步操作?,f#,workflow,computation-expression,F#,Workflow,Computation Expression,我想创建一个构建器,它构建的表达式在每个步骤后都会返回类似于continuation的内容 大概是这样的: module TwoSteps = let x = stepwise { let! y = "foo" printfn "got: %A" y let! z = y + "bar" printfn "got: %A" z return z } printfn "two steps" let a = x() printfn "s
module TwoSteps =
let x = stepwise {
let! y = "foo"
printfn "got: %A" y
let! z = y + "bar"
printfn "got: %A" z
return z
}
printfn "two steps"
let a = x()
printfn "something inbetween"
let b = a()
其中“let a”行返回包含后面要计算的其余表达式的内容
对于每个步骤数,使用单独的类型执行此操作非常简单,但当然不是特别有用:
type Stepwise() =
let bnd (v: 'a) rest = fun () -> rest v
let rtn v = fun () -> Some v
member x.Bind(v, rest) =
bnd v rest
member x.Return v = rtn v
let stepwise = Stepwise()
module TwoSteps =
let x = stepwise {
let! y = "foo"
printfn "got: %A" y
let! z = y + "bar"
printfn "got: %A" z
return z
}
printfn "two steps"
let a = x()
printfn "something inbetween"
let b = a()
module ThreeSteps =
let x = stepwise {
let! y = "foo"
printfn "got: %A" y
let! z = y + "bar"
printfn "got: %A" z
let! z' = z + "third"
printfn "got: %A" z'
return z
}
printfn "three steps"
let a = x()
printfn "something inbetween"
let b = a()
printfn "something inbetween"
let c = b()
结果就是我想要的:
two steps
got: "foo"
something inbetween
got: "foobar"
three steps
got: "foo"
something inbetween
got: "foobar"
something inbetween
got: "foobarthird"
但我不知道一般情况会是什么
我希望能够将事件反馈到此工作流中,这样您就可以编写如下内容:
let someHandler = Stepwise<someMergedEventStream>() {
let! touchLocation = swallowEverythingUntilYouGetATouch()
startSomeSound()
let! nextTouchLocation = swallowEverythingUntilYouGetATouch()
stopSomeSound()
}
让someHandler=Stepwise(){
let!touchLocation=吞下所有东西直到UgetaTouch()
startSomeSound()
让!nextTouchLocation=吞下所有东西直到UgetTouch()
停止声音
}
并让事件触发工作流中的下一步。(特别是,我想在iPhone上用MonoTouch-F#玩这种东西。传递objc选择器让我发疯。)Monads和计算构建器把我搞糊涂了,但我已经改编了我在一个。也许有些零碎的东西会有用 下面的代码包含一个操作队列和一个表单,单击事件在其中侦听操作队列中可用的下一个操作。下面的代码是一个连续执行4个操作的示例。在FSI中执行它并开始单击表单
open System.Collections.Generic
open System.Windows.Forms
type ActionQueue(actions: (System.EventArgs -> unit) list) =
let actions = new Queue<System.EventArgs -> unit>(actions) //'a contains event properties
with
member hq.Add(action: System.EventArgs -> unit) =
actions.Enqueue(action)
member hq.NextAction =
if actions.Count=0
then fun _ -> ()
else actions.Dequeue()
//test code
let frm = new System.Windows.Forms.Form()
let myActions = [
fun (e:System.EventArgs) -> printfn "You clicked with %A" (e :?> MouseEventArgs).Button
fun _ -> printfn "Stop clicking me!!"
fun _ -> printfn "I mean it!"
fun _ -> printfn "I'll stop talking to you now."
]
let aq = new ActionQueue(myActions)
frm.Click.Add(fun e -> aq.NextAction e)
//frm.Click now executes the 4 actions in myActions in order and then does nothing on further clicks
frm.Show()
您的实现的问题在于,它为每个要绑定的调用返回“unit->'a”,因此对于不同的步骤数,您将得到不同类型的结果(通常,这是monad/计算表达式的可疑定义)
正确的解决方案应该是使用其他类型,它可以表示具有任意步数的计算。您还需要区分两种类型的步骤——有些步骤只计算计算的下一步,有些步骤返回结果(通过
return
关键字)。我将使用类型seq在某种程度上,我试图得到的是使用工作流来构建手工操作的东西。工作流将生成一个函数序列,然后您只需迭代该序列。只是一个补充,这也非常类似于所谓的恢复单子。但是我相信在F#中使用序列的实现可能会更容易一些。“PS:我发现这个问题非常有趣!你介意我将我的答案添加到我的博客()的博客帖子中吗?”当然,请随意。谢谢你的回答;我现在正在研究如何将其应用到iPhone事件系统中。
let moreActions = [
fun _ -> printfn "Ok, I'll talk to you again. Just don't click anymore, ever!"
fun _ -> printfn "That's it. I'm done with you."
]
moreActions |> List.iter (aq.Add)
let foo() = stepwise {
return 1; }
let bar() = stepwise {
let! a = foo()
return a + 10 }
type Stepwise() =
member x.Bind(v:seq<option<_>>, rest:(_ -> seq<option<_>>)) = seq {
let en = v.GetEnumerator()
let nextVal() =
if en.MoveNext() then en.Current
else failwith "Unexpected end!"
let last = ref (nextVal())
while Option.isNone !last do
// yield None for each step of the source 'stepwise' computation
yield None
last := next()
// yield one more None for this step
yield None
// run the rest of the computation
yield! rest (Option.get !last) }
member x.Return v = seq {
// single-step computation that yields the result
yield Some(v) }
let stepwise = Stepwise()
// simple function for creating single-step computations
let one v = stepwise.Return(v)
let oneStep = stepwise {
// NOTE: we need to explicitly create single-step
// computations when we call the let! binder
let! y = one( "foo" )
printfn "got: %A" y
return y + "bar" }
let threeSteps = stepwise {
let! x = oneStep // compose computations :-)
printfn "got: %A" x
let! y = one( x + "third" )
printfn "got: %A" y
return "returning " + y }
for step, idx in Seq.zip threeSteps [ 1 .. 10] do
printf "STEP %d: " idx
match step with
| None _ -> ()
| Some(v) -> printfn "Final result: %s" v