异步 – F#异步工作流/任务与免费monad相结合
我正在尝试使用免费的monad模式构建用于消息处理的管道,我的代码看起来像这样:
module PipeMonad = type PipeInstruction<'msgIn,'msgOut,'a> = | HandleAsync of 'msgIn * (Async<'msgOut> -> 'a) | SendOutAsync of 'msgOut * (Async -> 'a) let private mapInstruction f = function | HandleAsync (x,next) -> HandleAsync (x,next >> f) | SendOutAsync (x,next) -> SendOutAsync (x,next >> f) type PipeProgram<'msgIn,'a> = | Act of PipeInstruction<'msgIn,PipeProgram<'msgIn,'a>> | Stop of 'a let rec bind f = function | Act x -> x |> mapInstruction (bind f) |> Act | Stop x -> f x type PipeBuilder() = member __.Bind (x,f) = bind f x member __.Return x = Stop x member __.Zero () = Stop () member __.ReturnFrom x = x let pipe = PipeBuilder() let handleAsync msgIn = Act (HandleAsync (msgIn,Stop)) let sendOutAsync msgOut = Act (SendOutAsync (msgOut,Stop)) 我根据this article写的 然而,让这些方法异步是很重要的(最好是Task,但Async是可以接受的),但是当我为我的管道创建一个构建器时,我无法弄清楚如何使用它 – 我怎样才能等待任务<'msgOut> ;或Async<'msgOut>所以我可以发送出去等待这个“发送”任务? 现在我有这段代码: let pipeline log msgIn = pipe { let! msgOut = handleAsync msgIn let result = async { let! msgOut = msgOut log msgOut return sendOutAsync msgOut } return result } 返回PipeProgram<'b,'a,Async< PipeProgram<'c,Async>>> 解决方法
在我的理解中,自由monad的重点在于你没有暴露像Async这样的效果,所以我不认为它们应该在PipeInstruction类型中使用.解释器是添加效果的地方.
此外,Free Monad真的只在Haskell中有意义,你需要做的就是定义一个仿函数,然后你自动完成其余的实现.在F#中,您还必须编写其余的代码,因此使用Free比传统的解释器模式没有多大好处. 最后,如果您已经知道将要使用的效果,并且您不会有多个解释,那么使用这种方法是没有意义的.只有当收益超过复杂性时才有意义. 无论如何,如果你确实想要编写一个解释器版本(而不是Free),我就是这样做的: 首先,定义指令而不产生任何影响. /// The abstract instruction set module PipeProgram = type PipeInstruction<'msgIn,'state> = | Handle of 'msgIn * ('msgOut -> PipeInstruction<'msgIn,'state>) | SendOut of 'msgOut * (unit -> PipeInstruction<'msgIn,'state>) | Stop of 'state 然后你可以为它编写一个计算表达式: /// A computation expression for a PipeProgram module PipeProgramCE = open PipeProgram let rec bind f instruction = match instruction with | Handle (x,next) -> Handle (x,(next >> bind f)) | SendOut (x,next) -> SendOut (x,(next >> bind f)) | Stop x -> f x type PipeBuilder() = member __.Bind (x,f) = bind f x member __.Return x = Stop x member __.Zero () = Stop () member __.ReturnFrom x = x let pipe = PipeProgramCE.PipeBuilder() 然后你就可以开始编写你的计算表??达式了.这将有助于在开始使用解释器之前清除设计. // helper functions for CE let stop x = PipeProgram.Stop x let handle x = PipeProgram.Handle (x,stop) let sendOut x = PipeProgram.SendOut (x,stop) let exampleProgram : PipeProgram.PipeInstruction<string,string,string> = pipe { let! msgOut1 = handle "In1" do! sendOut msgOut1 let! msgOut2 = handle "In2" do! sendOut msgOut2 return msgOut2 } 一旦描述了说明,就可以编写解释器.正如我所说,如果你不是在写多个口译员,那么也许你根本不需要这样做. 这是一个非异步版本的解释器(“Id monad”,就像它一样): module PipeInterpreterSync = open PipeProgram let handle msgIn = printfn "In: %A" msgIn let msgOut = System.Console.ReadLine() msgOut let sendOut msgOut = printfn "Out: %A" msgOut () let rec interpret instruction = match instruction with | Handle (x,next) -> let result = handle x result |> next |> interpret | SendOut (x,next) -> let result = sendOut x result |> next |> interpret | Stop x -> x 这是异步版本: module PipeInterpreterAsync = open PipeProgram /// Implementation of "handle" uses async/IO let handleAsync msgIn = async { printfn "In: %A" msgIn let msgOut = System.Console.ReadLine() return msgOut } /// Implementation of "sendOut" uses async/IO let sendOutAsync msgOut = async { printfn "Out: %A" msgOut return () } let rec interpret instruction = match instruction with | Handle (x,next) -> async { let! result = handleAsync x return! result |> next |> interpret } | SendOut (x,next) -> async { do! sendOutAsync x return! () |> next |> interpret } | Stop x -> x (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |