F#对嵌套计算表达式的调用太多

F#对嵌套计算表达式的调用太多,f#,monads,computation-expression,F#,Monads,Computation Expression,这是一个进化的问题。我试图找出为什么在CE上运行State.exec时,会出现CEs不希望出现的嵌套行为。它似乎给他们打了很多次电话。以下是我所拥有的: type State=('s->'a*'s) 模块状态= //明确的 //让结果x:State=funs->x,s //不太明确,但能更好地与其他现有功能配合使用: 让结果x s= x、 让bind(f:'a->State)(m:State):State= //返回获取状态的函数 乐趣s-> //从m参数中获取值和下一个状态 设a,s'=ms

这是一个进化的问题。我试图找出为什么在CE上运行
State.exec
时,会出现CEs不希望出现的嵌套行为。它似乎给他们打了很多次电话。以下是我所拥有的:

type State=('s->'a*'s)
模块状态=
//明确的
//让结果x:State=funs->x,s
//不太明确,但能更好地与其他现有功能配合使用:
让结果x s=
x、 
让bind(f:'a->State)(m:State):State=
//返回获取状态的函数
乐趣s->
//从m参数中获取值和下一个状态
设a,s'=mss
//通过将a传递给f参数来获取下一个状态计算
设m'=fa
//将下一个状态应用于下一个计算
m's'
///计算计算,返回结果值。
让评估(m:州)(s:'s)=
m s
|>fst
///执行计算,返回最终状态。
让执行官(m:州)(s:'s)=
m s
|>snd
///将状态作为值返回。
让getState(s:'s)=
s、 
///忽略为支持提供的状态值而传递的状态。
让我们陈述一下
乐趣->
(),s
类型StateBuilder()=
成员返回(值):状态=
State.result值
成员绑定(m:State,f:'a->State):State=
State.bind f m
成员uuu.ReturnFrom(m:州)=
M
成员0()=
State.result()
成员延迟(f)=
State.bind f(State.result())
设rng=System.Random(123)
类型StepId=int的StepId
定型食品=
|鸡
|米饭
类型步骤=
|获取StepId的食物*食物
|吃StepId*食物
|StepId的睡眠*持续时间:int
类型PlanAcc=lastStepId的PlanAcc:StepId*步骤:步骤列表
让state=StateBuilder()
让我们吃点东西吧=
陈述{
打印fn“获取食物”
让我们吃点东西=
如果rng.NextDouble()>0.5,则食物。鸡肉
其他食物,米饭
让!(PlanAcc(StepId lastStepId,steps))=State.getState
设nextStepId=StepId(lastStepId+1)
let newStep=GetFood(下一步,随机食物)
让newAcc=PlanAcc(nextStepId,newStep::steps)
do!State.setState newAcc
归还食物
}
使用
[]
成员this.Sleep(st:State,[](duration:'a->int))=
printfn$“睡眠”
让程序d=
陈述{
设!x=st
printfn“睡眠:%A”持续时间
让!(PlanAcc(StepId lastStepId,steps))=State.getState
设nextStepId=StepId(lastStepId+1)
让newStep=Sleep(nextStepId,d)
让newAcc=PlanAcc(nextStepId,newStep::steps)
do!State.setState newAcc
返回x
}
State.bind(乐趣x->program(持续时间x))st
[]
成员this.Eat(st:State,[](food:'a->food))=
printfn$“吃”
让程序e=
陈述{
设!x=st
printfn“吃:%A”食物
让!(PlanAcc(StepId lastStepId,steps))=State.getState
设nextStepId=StepId(lastStepId+1)
让newStep=Eat(nextStepId,e)
让newAcc=PlanAcc(nextStepId,newStep::steps)
do!State.setState newAcc
返回x
}
State.bind(funx->program(food x))st
让simplePlan=
陈述{
让我们一起吃吧
睡眠1
吃f1
睡眠2
吃f1
睡眠3
}
让initalAcc=PlanAcc(步骤ID 0,[])
设x=State.exec simplePlan initalAcc
以下是我希望通过
x
获得的:

> x;;
val it : PlanAcc =
PlanAcc
(StepId 6,
  [Sleep (StepId 6, 3); GetFood (StepId 5, Chicken);
      Sleep (StepId 4, Chicken); EatFood (StepId 3, Chicken);
      Sleep (StepId 2, 1); GetFood (StepId 1, Chicken)])
以下是我得到的:

> x;;
val it : PlanAcc =
  PlanAcc
    (StepId 63,
     [Sleep (StepId 63, 3); Eat (StepId 62, Rice); Sleep (StepId 61, 2);
      Eat (StepId 60, Rice); Sleep (StepId 59, 1);
      GetFood (StepId 58, Chicken); GetFood (StepId 57, Chicken);
      Sleep (StepId 56, 1); GetFood (StepId 55, Rice);
      GetFood (StepId 54, Chicken); Eat (StepId 53, Chicken);
      Sleep (StepId 52, 1); GetFood (StepId 51, Chicken);
      GetFood (StepId 50, Chicken); Sleep (StepId 49, 1);
      GetFood (StepId 48, Chicken); GetFood (StepId 47, Chicken);
      Sleep (StepId 46, 2); Eat (StepId 45, Rice); Sleep (StepId 44, 1);
      GetFood (StepId 43, Rice); GetFood (StepId 42, Chicken);
      Sleep (StepId 41, 1); GetFood (StepId 40, Rice);
      GetFood (StepId 39, Rice); Eat (StepId 38, Rice); Sleep (StepId 37, 1);
      GetFood (StepId 36, Chicken); GetFood (StepId 35, Rice);
      Sleep (StepId 34, 1); GetFood (StepId 33, Rice);
      GetFood (StepId 32, Chicken); Eat (StepId 31, Rice);
      Sleep (StepId 30, 2); Eat (StepId 29, Rice); Sleep (StepId 28, 1);
      GetFood (StepId 27, Chicken); GetFood (StepId 26, Rice);
      Sleep (StepId 25, 1); GetFood (StepId 24, Rice);
      GetFood (StepId 23, Rice); Eat (StepId 22, Chicken);
      Sleep (StepId 21, 1); GetFood (StepId 20, Rice);
      GetFood (StepId 19, Chicken); Sleep (StepId 18, 1);
      GetFood (StepId 17, Chicken); GetFood (StepId 16, Rice);
      Sleep (StepId 15, 2); Eat (StepId 14, Rice); Sleep (StepId 13, 1);
      GetFood (StepId 12, Rice); GetFood (StepId 11, Rice);
      Sleep (StepId 10, 1); GetFood (StepId 9, Rice);
      GetFood (StepId 8, Chicken); Eat (StepId 7, Chicken);
      Sleep (StepId 6, 1); GetFood (StepId 5, Chicken);
      GetFood (StepId 4, Chicken); Sleep (StepId 3, 1);
      GetFood (StepId 2, Chicken); GetFood (StepId 1, Chicken)])
我相当确定这与
程序中
状态的绑定方式有关,因为只要调用
let,我就没有问题!f=getFood
多次

我试图删除
let!x=st
程序中返回x
调用
CEs认为这是导致问题的原因,但编译器在
simplePlan
CE中抱怨说,“表达式应该有'Food'类型,但这里有'unit'类型”

错误图像:

是的,你是对的:它确实与在
let处绑定传入的计算有关!x=st

但您也正确地认识到,您不能仅仅删除该绑定,因为您需要通过隧道传递其返回值,正如我在中所描述的

但是这
让我们来看看!x=st
本身不是问题

问题是您正在绑定
st
两次:


    [<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
    member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
        printfn $"Eat"
        let program e =
            state {
                let! x = st  <-- here's the first time
                printfn "Eat: %A" food
                let! (PlanAcc (StepId lastStepId, steps)) = State.getState
                let nextStepId = StepId (lastStepId + 1)
                let newStep = Eat (nextStepId, e)
                let newAcc = PlanAcc (nextStepId, newStep::steps)
                do! State.setState newAcc
                return x
            }
        State.bind (fun x -> program (food x)) st
                                               ^^
                                               here's the second time

神圣的烟。这比我想象的要简单得多。非常感谢。这两个问题的答案对我理解CEs的作用比我读到的其他很多东西都要大。非常感谢。再一次,很高兴我能帮忙。
    [<CustomOperation("eat", MaintainsVariableSpaceUsingBind=true)>]
    member this.Eat (st:State<_,PlanAcc>, [<ProjectionParameter>] (food: 'a -> Food)) =
        printfn $"Eat"
        state {
            let! x = st
            let f = food x
            printfn "Eat: %A" f
            let! (PlanAcc (StepId lastStepId, steps)) = State.getState
            let nextStepId = StepId (lastStepId + 1)
            let newStep = Eat (nextStepId, f)
            let newAcc = PlanAcc (nextStepId, newStep::steps)
            do! State.setState newAcc
            return x
        }