【发布时间】:2020-06-19 15:43:13
【问题描述】:
我正在尝试在 F# 中编写代码,以允许登录到自定义源并使用生成随机数和可交换实现。
我没有将日志记录函数/随机生成器传递给应用程序中的每个函数,而是尝试将这些函数作为计算表达式的上下文传递。
我注意到它与 state monad 实现非常相似,所以我尝试编写类似的东西。
我的概念证明工作正常,生成数字的工作非常好,但我无法使其与我的日志记录函数的 printf 样式参数很好地工作:
module Test
type Simulator = { random : int * int -> int; logger : string -> unit }
module Simulator =
let create = let random = new System.Random() in
{
random = fun (min, max) -> random.Next(min, max)
logger = fun str -> (printfn "%s" str |> ignore)
}
type Simulation<'T> = Simulation of (Simulator -> 'T)
module Simulation =
/// Runs a simulation given a simulator
let inline run state simulation = let (Simulation(play)) = simulation in play state
/// Returns a random number
let random min max = Simulation (fun simulator -> simulator.random (min, max))
/// Writes to simulation log
let log = Simulation (fun simulation -> Printf.ksprintf simulation.logger)
type SimulationBuilder() =
member this.Bind (x, f) = let (Simulation(simulation)) = x in Simulation (fun simulator -> f (simulation simulator))
member this.Return (x) = x
let simulate = new SimulationBuilder()
let simpleSimulation =
simulate
{
//very nice, working
let! x = Simulation.random 2 12
//this is working, but verbose
let! logger = Simulation.log
do logger "Value: %d" x
//I want to write
//do! Simulation.log "Value: %d" x
// or something similar
return x;
}
Simulation.run Simulator.create simpleSimulation |> ignore
有人可以帮助我吗?我对编写自定义计算表达式很陌生。
编辑
注意日志函数可以有签名
let log str = Simulation (fun simulation -> simulation.logger str)
而且很容易调用:
simulate
{
...
do! Simulation.log "Hello world!"
...
}
但是在这里我失去了在不使用 sprintf 的情况下传递格式参数的能力
编辑
bind实现有错误,应该是:
member this.Bind (x, f) = let (Simulation(simulation)) = x in Simulation (fun simulator -> Simulation.run simulator (f (simulation simulator)))
【问题讨论】:
标签: f# monads computation-expression