2

我写简单的解释器,我想存储变量。到目前为止,我有:

-- MyEnv is a map from strings into integers
type MyEnv = M.Map String Int
type TCM a = ErrorT String (StateT MyEnv IO) a

我有一个定义

ms_assgn :: Assgn -> TCM()
ms_assgn (Assgn (Ident ident) exp) = do
    map <- get
    w1 <- ms_exp exp
    put (M.insert (ident w1 map))

我得到了以下错误:

Interpret.hs:118:5:
Couldn't match type `Envnt' with `a0 -> M.Map k0 a0 -> M.Map k0 a0'
When using functional dependencies to combine
  MonadState s (StateT s m),
    arising from the dependency `m -> s'
    in the instance declaration in `Control.Monad.State.Class'
  MonadState (a0 -> M.Map k0 a0 -> M.Map k0 a0) (StateT Envnt IO),
    arising from a use of `put' at Interpret.hs:118:5-7
In a stmt of a 'do' block: put (M.insert (ident w1 map))
In the expression:
  do { map <- get;
       w1 <- ms_exp exp;
       put (M.insert (ident w1 map)) }

Interpret.hs:118:20:
Couldn't match expected type `Integer -> Envnt -> k0'
            with actual type `[Char]'
The function `ident' is applied to two arguments,
but its type `String' has none
In the first argument of `M.insert', namely `(ident w1 map)'
In the first argument of `put', namely `(M.insert (ident w1 map))'

当我用 put 注释掉最后一行并用 return() 替换它时,它没有任何合理性,但至少它可以编译。我这样理解的 ms_assgn 函数:

  • 首先我得到我目前的状态
  • 接下来我将 exp 评估为 w1
  • 最后我想更新我的状态

它有什么问题?有什么提示吗?

4

1 回答 1

9

这只是一组额外的括号。

M.insert (ident w1 map) -- wrong

insert函数具有 type k -> a -> Map k a -> Map k a,但那些额外的括号意味着您正在调用ident它,就好像它是一个函数一样。

M.insert ident w1 map -- correct

但是,作为语义问题,如果ms_exp exp修改环境,您可能会遇到意外行为,因为这些更改会丢失。我会将其移至环境修改之上:

ms_assgn (Assgn (Ident ident) exp) = do
  w1 <- ms_exp exp
  map <- get
  put $ M.insert ident w1 map

并且 aget后跟 aput可以更改为 a modify,柯里化insert。顺便说一句,如果您想知道为什么 theMap k a是 的最后一个参数insert,这就是原因。

ms_assgn (Assgn (Ident ident) exp) = do
  w1 <- ms_exp exp
  modify $ M.insert ident w1

如果您愿意,您可以确定这两do行实际上只是一个单行>>=,所以...

ms_assgn (Assgn (Ident ident) exp) =
  ms_exp exp >>= modify . M.insert ident

您可以看到数据如何而不是使用命令式,而是do通过一元绑定运算符>>=流入修改环境的操作。

于 2013-05-04T18:44:35.143 回答