在非IO环境中执行IO操作

时间:2016-08-13 21:37:30

标签: haskell

我已经陷入了以下困境。

evalAExpr :: AExpr -> Env -> Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
  where
    actAccordingly (Left int) = int
    actAccordingly (Right stmt) = eval stmt env

eval :: Stmt -> Env ->  IO Env`

getValueOfBinding :: Env -> String -> Either Integer Stmt

所以需要发生的是,从Either Monad得到的Stmt x,它应该评估x(由于其他进程,需要在IO中进行评估)进入一个新的环境,之后我可以从环境中返回正确的值。但是,根据我对Haskell的有限知识,我无法摆脱IO

例如:givetheCorrectThing nameOfTheValueInTheEnv (eval stmt env)永远不会使用当前代码,因为所有evalAExpr函数(是的,只有1个)都返回一个Env atm。如果我必须改变它,它将导致添加所有评估do和return语句。 > 50-60次出现。当然。这会使代码看起来如此混乱。我需要IO的原因是因为这个评估必须(在某个时候)并从mBot(http://makeblock.com/mbot-stem-educational-robot-kit-for-kids/)收集数据,以及从键盘收集数据(通过getLine完成)

你们中的任何人都有办法重新编写这些代码,以便我能保持整洁有序吗?

如果需要更多代码:请随意ping /提及我。我将提供所要求的代码行。

根据要求提供更多代码:

  • 显示如何使用EvalAExpr的代码。

-- Relational operators evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env evalBExpr (RBinary Less a b) env = evalAExpr a env < evalAExpr b env evalBExpr (RBinary Equal a b) env = evalAExpr a env == evalAExpr b env

2 个答案:

答案 0 :(得分:2)

  

从我对Haskell的有限知识中,我无法摆脱IO

正确!并且有充分的理由:使用当前的签名,任何使用你的函数的人都知道这真的只是一个纯粹的,引用透明的计算,所以他们可以相应地粗心大意(懒惰等)。如果你被允许只通过签名放入一些IO而没有明确说明,你可能很容易打破每个人的代码!

当然,如果你只是注意到你需要做IO,那么你确实处于一种两难的境地。现在没有好办法改变所有签名。

但是,你可以通过首先使用类型同义词来阻止这种情况:如果你原来是

type Evaluation a = Identity a

-- ... lots of functions with an `Evaluation` result ...

evalAExpr :: AExpr -> Env -> Evaluation Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
  where
    actAccordingly (Left int) = return int
    actAccordingly (Right stmt) = someThingThatDidn'tYetRequireIO

然后,在注意到您需要IO之后,您可能刚刚将其更改为

type Evaluation a = IO a

{- same as before:
evalAExpr :: AExpr -> Env -> Evaluation Int
evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
  where
    actAccordingly (Left int) = return int -}
    actAccordingly (Right stmt) = eval stmt env

在一个大项目中,Evaluation monad通常是一个monad变换器堆栈,你只需为它下面的IO添加一个额外的层。

如果有人提醒我function that shall not be named,我会打击他们关于头with a nuclear missile

正如dfeuer所说,newtype可能更好。

答案 1 :(得分:1)

您必须编写evalAExpr

的monadic版本

我们会调用新版本evalAExprIO

evalAExprIO :: AExpr -> Env -> IO Int
 evalAExpr (Var x) env = actAccordingly (getValueOfBinding env x)
  where
    actAccordingly (Left int) = return int
    actAccordingly (Right stmt) = eval stmt env

实际上只有一个重大变化 - 在Left案例中 函数actAccordingly使用return int而不是int

evalAExprIO的类型签名已更改,但这是一个问题 GHC可以推断,所以你不必知道如何改变。

<强>更新

变化:

evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env

为:

evalBexpr (RBinary Greater a b) env = liftM2 (>) (evalAExpr a env) (evalExpr b env)

(为liftM2导入Control.Monad。)