【发布时间】:2021-03-18 17:27:30
【问题描述】:
这是我在此处提出的问题 (F# Binding to output while carrying the state) 的演变。
我正在尝试让Bind 方法获取具有此签名PlanAccumulator<'a> -> PlanAccumulator<'b> 的函数。 getFood 函数就是一个例子。我希望Bind 方法使用PlanAccumulator<'a> 对象调用getFood。
挑战在于让计算表达式 (CE) 使用 CE 中该点存在的 PlanAccumulator 调用 Bind 方法内的 getFood 函数。我不知道该怎么做,但我觉得应该可以。
type StepId = StepId of int
type State = {
LastStepId : StepId
}
type Food =
| Chicken
| Rice
type Step =
| GetFood of StepId * Food
| Eat of StepId * Food
| Sleep of StepId * duration:int
type PlanAccumulator<'T> = PlanAccumulator of State * Step list * 'T
let rng = System.Random(123)
let getFood (PlanAccumulator (s, p, r)) =
printfn "GetFood"
let randomFood =
if rng.NextDouble() > 0.5 then Food.Chicken
else Food.Rice
let (StepId lastStepId) = s.LastStepId
let nextStepId = StepId (lastStepId + 1)
let newState = { s with LastStepId = nextStepId }
let newStep = GetFood (nextStepId, randomFood)
PlanAccumulator (newState, newStep::p, randomFood)
type PlanBuilder (state: State) =
member this.For (PlanAccumulator (state, steps1, res):PlanAccumulator<'T>, f:'T -> PlanAccumulator<'R>) : PlanAccumulator<'R> =
printfn "For"
let (PlanAccumulator(state2, steps2, res2)) = f res
PlanAccumulator (state2, steps2 @ steps1, res2)
member this.Bind (input:PlanAccumulator<'a> -> PlanAccumulator<'T>, f:'T -> PlanAccumulator<'R>) : PlanAccumulator<'R> =
printfn "Bind"
// THIS IS THE PROBLEM: How do I get the previous PlanAccumulator to
// this point in the computation?
let PlanAccumulator (state1, steps1, res) = input previousAccumulator
let (PlanAccumulator(state2, steps2, res2)) = f (state1 res)
PlanAccumulator (state2, steps2 @ steps1, res2)
member this.Yield x =
printfn "Yield"
PlanAccumulator (state, [], x)
member this.Run (PlanAccumulator (s, p, r)) =
printfn "Run"
s, List.rev p
[<CustomOperation("eat", MaintainsVariableSpace=true)>]
member this.Eat (PlanAccumulator(s, p, r), [<ProjectionParameter>] food) =
printfn $"Eat: {food}"
let (StepId lastStepId) = s.LastStepId
let nextStepId = StepId (lastStepId + 1)
let newState = { s with LastStepId = nextStepId }
let newStep = Eat (nextStepId, (food r))
PlanAccumulator (newState, newStep::p, r)
[<CustomOperation("sleep", MaintainsVariableSpace=true)>]
member this.Sleep (PlanAccumulator (s, p, r), [<ProjectionParameter>] duration) =
printfn $"Sleep: {duration}"
let (StepId lastStepId) = s.LastStepId
let nextStepId = StepId (lastStepId + 1)
let newState = { s with LastStepId = nextStepId }
let newStep = Sleep (nextStepId, (duration r))
PlanAccumulator (newState, newStep::p, r)
// let plan = PlanBuilder()
let initialState = {
LastStepId = StepId 0
}
let newState, testPlan =
PlanBuilder initialState {
let! food = getFood
sleep 5
eat Chicken
}
以下是testPlan 的示例,如果它按预期工作:
val testPlan : Step list =
[
(StepId 1, GetFood Chicken)
(StepId 2, Sleep 1)
(StepId 3, Eat Chicken)
]
【问题讨论】:
标签: f# monads computation-expression