2017-02-09 112 views
1

我要通过国家单子here,我想要实现:实现状态monad时数据构造函数错误?

import Control.Monad.Reader 
import Control.Monad.Writer 
import Control.Monad.State 

type Stack = [Int] 

pop :: State Stack Int 
pop = State $ (x : xs) -> (x, xs) 

不过,我发现了以下错误:

"Data constructor not in scope: 
    State :: ([t0] -> (t0, [t0])) -> State Stack Int 
Perhaps you meant one of these: 
    ‘StateT’ (imported from Control.Monad.State), 
    variable ‘state’ (imported from Control.Monad.State)" 

我在这里缺少一些基本的东西?

回答

4

不,你不是。这个教程简化了一些东西(或者它可能只是过时了 - 我没有足够的了解这两个中的哪一个已经过时了)。 Control.Monad.State定义了一个单变量变压器StateT。它还导出一个简单的类型同义词相当于什么教程教你

type State s a = StateT s Identity a 

然而,这并不意味着构造不State,它是StateT(它有一个广义的签名)。谢天谢地,您现在不需要担心太多。

  • 对于构建State,您可以使用state功能,并假装它具有签名state :: (s -> (a,s)) -> State s a(实际上它有一个更一般的签名 - 你在错误消息中遇到)。
  • 对于解构State,只需使用runState :: State s a -> s -> (a,s)而不是模式匹配。

从这个例子中你给了:

import Control.Monad.Reader 
import Control.Monad.Writer 
import Control.Monad.State 

type Stack = [Int] 

pop :: State Stack Int 
pop = state $ \(x : xs) -> (x, xs) 
+1

谢谢亚历克,非常清楚 - 我很高兴我不会在我年老时挑剔! –

+0

[链接的教程已过时,而不是简化的事情。](http://hackage.haskell.org/package/mtl-1.0/docs/Control-Monad-State。html#t:State)有一段时间,人们担心与'Identity'相关的变换器相比直接定义相应的monad具有运行时成本,并且包含很多库(包括mtl)会同时发布monad及其变换器。最终代码复制/维护负担的论点反对这一点胜过了它的性能论证。 –

2

为什么你认为State a的接口是通过一个数据构造函数来包装函数s -> (a, s)?这是一个简单的方法来实现状态,但你没有提供该接口。相反,请使用Control.Monad.State中提供的构造。

一个简单的变化就是使用小写state功能设计用于此目的:

pop :: State Stack Int 
pop = state $ \(x : xs) -> (x, xs) 

或者,而不是用国家的这种低层次的视图时,您可以使用它作为一个通过其putget功能单子:

pop :: State Stack Int 
pop = do 
    (x : xs) <- get 
    put xs 
    return x 
+2

她是假设接口'State'因为那是教程说什么。 :/“Control.Monad.State'模块提供了一个包装有状态计算的新类型,下面是它的定义:'newtype State s a = State {runState :: s - >(a,s)}'”。 – Alec

+0

我不知道一个模式的同义词是否可取,所以用一个简单的界面导出一个简单的“State”的“幻觉”。我认为,使用安全强制,它应该具有零运行时成本。这可以扩展到应用于“身份”的大多数单变换器。 – chi