mon*_*res 1 monads f# state state-monad
我正在尝试围绕 F# 中的 mon-、err、工作流进行思考,虽然我认为我对基本的“可能”工作流有相当深入的了解,但尝试实现状态工作流来生成随机数确实让我受益匪浅难倒。
我未完成的尝试可以在这里看到:
let randomInt state =
let random = System.Random(state)
// Generate random number and a new state as well
random.Next(0,1000), random.Next()
type RandomWF (initState) =
member this.Bind(rnd,rest) =
let value, newState = rnd initState
// How to feed "newState" into "rest"??
value |> rest
member this.Return a = a // Should I maybe feed "initState" into the computation here?
RandomWF(0) {
let! a = randomInt
let! b = randomInt
let! c = randomInt
return [a; b; c]
} |> printfn "%A"
Run Code Online (Sandbox Code Playgroud)
编辑:实际上得到了它的工作!虽然不完全确定它是如何工作的,所以如果有人想把它放在一个好的答案中,它仍然可以争夺。这是我的工作代码:
type RandomWF (initState) =
member this.Bind(rnd,rest) =
fun state ->
let value, nextState = rnd state
rest value nextState
member this.Return a = fun _ -> a
member this.Run x = x initState
Run Code Online (Sandbox Code Playgroud)
有两件事让您更难了解您的工作流程正在做什么:
我认为一旦您看到没有这两个障碍的情况,就会更清楚。这是使用 DU 包装器类型定义的工作流:
type Random<'a> =
Comp of (int -> 'a * int)
let run init (Comp f) = f init
type Random<'a> with
member this.Run(state) = fst <| run state this
type RandomBuilder() =
member this.Bind(Comp m, f: 'a -> Random<_>) =
Comp <| fun state ->
let value, nextState = m state
let comp = f value
run nextState comp
member this.Return(a) = Comp (fun s -> a, s)
let random = RandomBuilder()
Run Code Online (Sandbox Code Playgroud)
这是你如何使用它:
let randomInt =
Comp <| fun state ->
let rnd = System.Random(state)
rnd.Next(0,1000), rnd.Next()
let rand =
random {
let! a = randomInt
let! b = randomInt
let! c = randomInt
return [a; b; c ]
}
rand.Run(0)
|> printfn "%A"
Run Code Online (Sandbox Code Playgroud)
在这个版本中,您单独构建计算(并将其存储在 Random 类型中),然后在初始状态下运行它。查看构建器方法的类型是如何推断的,并将它们与MSDN 文档描述的内容进行比较。
编辑:构造一个构建器对象一次并将绑定用作各种别名主要是约定,但它是有道理的,因为构建器无状态是有意义的。我可以理解为什么参数化构建器似乎是一个有用的功能,但老实说,我无法想象一个令人信服的用例。
monad 的主要卖点是计算的定义和执行的分离。
在您的情况下 - 您希望能够做的是获取计算的表示并能够以某种状态运行它 - 可能是 0,也可能是 42。您不需要知道定义计算的初始状态那将使用它。通过将状态传递给构建器,您最终会模糊定义和执行之间的界限,这只会使工作流变得不那么有用。
将其与async工作流进行比较- 当您编写异步块时,您不会使代码异步运行。您只创建一个Async<'a>表示计算的对象,该对象将'a在您运行它时生成一个对象- 但是如何执行由您决定。建造者不需要知道。
| 归档时间: |
|
| 查看次数: |
434 次 |
| 最近记录: |