[英]F# rewrite computation expression
I'm studying continuations because I want to make some interesting use of coroutines... anyway, I want to better understand one implementation I found. 我正在研究延续,因为我想对协同程序进行一些有趣的使用......无论如何,我想更好地理解我发现的一个实现。
To do so I want to rewrite the implementation without using the computation expression (continuation Monad), but I'm not quite able to do it. 为此我想重写实现而不使用计算表达式(continuation Monad),但我不能完成它。
I have this: 我有这个:
type K<'T,'r> = (('T -> 'r) -> 'r)
let returnK x = (fun k -> k x)
let bindK m f = (fun k -> m (fun a -> f a k))
let runK (c:K<_,_>) cont = c cont
let callcK (f: ('T -> K<'b,'r>) -> K<'T,'r>) : K<'T,'r> =
fun cont -> runK (f (fun a -> (fun _ -> cont a))) cont
type ContinuationBuilder() =
member __.Return(x) = returnK x
member __.ReturnFrom(x) = x
member __.Bind(m,f) = bindK m f
member this.Zero () = this.Return ()
let K = new ContinuationBuilder()
/// The coroutine type from http://fssnip.net/7M
type Coroutine() =
let tasks = new System.Collections.Generic.Queue<K<unit,unit>>()
member this.Put(task) =
let withYield = K {
do! callcK (fun exit ->
task (fun () ->
callcK (fun c ->
tasks.Enqueue(c())
exit ())))
if tasks.Count <> 0 then
do! tasks.Dequeue() }
tasks.Enqueue(withYield)
member this.Run() =
runK (tasks.Dequeue()) ignore
// from FSharpx tests
let ``When running a coroutine it should yield elements in turn``() =
// This test comes from the sample on http://fssnip.net/7M
let actual = System.Text.StringBuilder()
let coroutine = Coroutine()
coroutine.Put(fun yield' -> K {
actual.Append("A") |> ignore
do! yield' ()
actual.Append("B") |> ignore
do! yield' ()
actual.Append("C") |> ignore
do! yield' ()
})
coroutine.Put(fun yield' -> K {
actual.Append("1") |> ignore
do! yield' ()
actual.Append("2") |> ignore
do! yield' ()
})
coroutine.Run()
actual.ToString() = "A1B2C"
``When running a coroutine it should yield elements in turn``()
So, I want rewrite the Put
member of the Coroutine class without using the computation expression K
. 所以,我想在不使用计算表达式
K
情况下重写Coroutine类的Put
成员。
I have read of course this and this and several other articles about catamorphisms but it is not quite easy to rewrite this continuation monand as it is to rewrite the Write Monad for example... 我当然读过这 本以及其他几篇关于catamorphisms的文章,但是重写这个延续monand并不是很容易,因为它是重写Write Monad的例子......
I try several ways, this is one of them: 我尝试了几种方法,这是其中之一:
member this.Put(task) =
let withYield =
bindK
(callcK (fun exit ->
task (fun () ->
callcK (fun c ->
tasks.Enqueue(c())
exit ()))))
(fun () ->
if tasks.Count <> 0
then tasks.Dequeue()
else returnK ())
tasks.Enqueue(withYield)
Of course it does not work :( 当然它不起作用:(
(By the way: there is some extensive documentation of all rules the compiler apply to rewrite the computation in plain F#?) (顺便说一句:有一些关于编译器应用于在普通F#中重写计算的所有规则的大量文档?)
Your version of Put
is almost correct. 您的
Put
版本几乎是正确的。 Two issues though: 但有两个问题:
bindK
function is being used backwards, the parameters need to be swaped. bindK
函数正在向后使用,需要交换参数。 task
should be passed a Cont<_,_> -> Cont<_,_>
, not a unit -> Cont<_,_> -> Cont<_,_>
. task
应该传递给Cont<_,_> -> Cont<_,_>
,而不是unit -> Cont<_,_> -> Cont<_,_>
。 Fixing those issues it could look like this: 修复这些问题可能如下所示:
member this.Put(task) =
let withYield =
bindK
(fun () ->
if tasks.Count <> 0
then tasks.Dequeue()
else returnK ())
(callcK (fun exit ->
task (
callcK (fun c ->
tasks.Enqueue(c())
exit ()))))
tasks.Enqueue(withYield)
Of course it is not too elegant. 当然它不是太优雅。 When using
bind
it is better to declare an operator >>=
: 使用
bind
,最好声明一个operator >>=
:
let (>>=) c f = bindK f c
that way 那样
do!
translates to putting >>= fun () ->
after >>= fun () ->
之后 let! a =
let! a =
translates to putting >>= fun a ->
after let! a =
转换为放置>>= fun a ->
after and then your code will look a little bit better: 然后你的代码看起来会更好一些:
member this.Put2(task) =
let withYield =
callcK( fun exit ->
task( callcK (fun c ->
tasks.Enqueue(c())
exit())
)
) >>= fun () ->
if tasks.Count <> 0 then
tasks.Dequeue()
else returnK ()
tasks.Enqueue withYield
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.