F# 中的随机/状态工作流
Random / State workflow in F#
我正在努力思考 F# 中的 mon-, err, 工作流,虽然我认为我对基本 "Maybe" 工作流有相当扎实的理解,但我正在尝试实现状态工作流以生成随机数真的让我难住了。
我未完成的尝试可以在这里看到:
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"
编辑:真的让它起作用了!虽然不确定它是如何工作的,所以如果有人想把它放在一个好的答案中,它仍然有待解决。这是我的工作代码:
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
有两件事让您更难看到您的工作流程在做什么:
- 您正在为您的 monad 类型使用函数类型,
- 您的工作流不仅建立了计算,而且运行它也是。
我认为,一旦您看到没有这两个障碍的情况,就会更清楚地遵循它。下面是使用 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()
下面是你如何使用它:
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"
在此版本中,您单独构建计算(并将其存储在 Random 类型中),然后 运行 将其传入初始状态。查看构建器方法的类型是如何推断出来的,并将它们与 MSDN documentation 描述的内容进行比较。
编辑:构造一次构建器对象并将绑定用作各种别名主要是约定俗成,但它有充分的理由,因为构建器是无状态的是有意义的.我明白为什么参数化构建器看起来是一个有用的特性,但老实说,我无法想象一个令人信服的用例。
monad 的主要卖点是计算的定义 和执行 的分离。
在你的情况下——你想要做的是对你的计算进行表示,并能够运行它具有某种状态——也许是 0,也许是 42。你不需要知道初始状态以定义将使用它的计算。通过将状态传递给构建器,您最终模糊了定义和执行之间的界限,这只会降低工作流的用处。
将其与 async
工作流程进行比较 - 当您编写异步块时,您不会使代码 运行 异步。您只创建一个 Async<'a>
对象来表示一个计算,当您 运行 它时,该计算将产生一个 'a
的对象 - 但是您如何做,取决于您。建造者不需要知道。
我正在努力思考 F# 中的 mon-, err, 工作流,虽然我认为我对基本 "Maybe" 工作流有相当扎实的理解,但我正在尝试实现状态工作流以生成随机数真的让我难住了。
我未完成的尝试可以在这里看到:
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"
编辑:真的让它起作用了!虽然不确定它是如何工作的,所以如果有人想把它放在一个好的答案中,它仍然有待解决。这是我的工作代码:
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
有两件事让您更难看到您的工作流程在做什么:
- 您正在为您的 monad 类型使用函数类型,
- 您的工作流不仅建立了计算,而且运行它也是。
我认为,一旦您看到没有这两个障碍的情况,就会更清楚地遵循它。下面是使用 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()
下面是你如何使用它:
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"
在此版本中,您单独构建计算(并将其存储在 Random 类型中),然后 运行 将其传入初始状态。查看构建器方法的类型是如何推断出来的,并将它们与 MSDN documentation 描述的内容进行比较。
编辑:构造一次构建器对象并将绑定用作各种别名主要是约定俗成,但它有充分的理由,因为构建器是无状态的是有意义的.我明白为什么参数化构建器看起来是一个有用的特性,但老实说,我无法想象一个令人信服的用例。
monad 的主要卖点是计算的定义 和执行 的分离。
在你的情况下——你想要做的是对你的计算进行表示,并能够运行它具有某种状态——也许是 0,也许是 42。你不需要知道初始状态以定义将使用它的计算。通过将状态传递给构建器,您最终模糊了定义和执行之间的界限,这只会降低工作流的用处。
将其与 async
工作流程进行比较 - 当您编写异步块时,您不会使代码 运行 异步。您只创建一个 Async<'a>
对象来表示一个计算,当您 运行 它时,该计算将产生一个 'a
的对象 - 但是您如何做,取决于您。建造者不需要知道。