我是哈斯克尔的新手,正在修改Stephen Diehl的《我希望我在学习哈斯克尔时知道什么》中的一些例子。
我一直停留在这个monad转换器的例子上:原始代码。
即使在将代码简化到最低限度之后,我也未能找出错误的原因。我还研究了SO上的其他问题,这些问题看起来很相似,但问题的原因似乎不同。
这是简化的代码:
import Control.Monad.Reader
type Env = [(String, Int)]
type Eval a = ReaderT Env Maybe a
data Expr
= Val Int
| Var String
deriving (Show)
eval :: Expr -> Eval Int
eval ex = case ex of
Val n -> return n
代码编译正确,但一旦我运行eval (Val 5)
GHCi,就会输出以下错误。
Prelude> eval (Val 5)
<interactive>:135:1: error:
• No instance for (Show (Eval Int)) arising from a use of ‘print’
• In a stmt of an interactive GHCi command: print it
谢谢。
不能打印类型为Eval Int
的值,因为它被定义为ReaderT Env Maybe Int
。打印该类型的值本质上相当于打印函数Env -> Maybe Int
,并且函数无法打印。
考虑使用调用函数
runReaderT (eval (Val 5)) []
其中CCD_ 5以上代表评估环境。
这并不是没有道理的,事实上它帮助我避免了将newtype包装/展开与实际数据混淆。
如果我们将ReaderT
定义为类型同义词,而不是newtype,那么它是一个我们可以直接应用的函数
type MyReaderT :: Type -> (Type -> Type) -> Type -> Type
type MyReaderT a m b = a -> m b
mypure :: Applicative f => b -> MyReaderT a f b
mypure b _a = pure b
你的eval
变成
type Eval :: Type -> Type
type Eval b = MyReaderT Env Maybe b
展开MyReaderT
:
type Eval :: Type -> Type
type Eval b = Env -> Maybe b
eval :: Expr -> Eval Int
eval (Val n) = mypure n
你可以直接通过环境
myeval (Val 3)
:: Env -> Maybe Int
myeval (Val 3) [("x", 4)]
= Just 3
:: Maybe Int
我的建议,尤其是对于monad转换器,是实现所有内容的my*
版本。newtype
允许我们编写实例,我们不能为MyReaderT ..
编写任何常见的实例,但它可以让您更接近实际问题。
对我来说,我在学校很难理解这样的东西
type Time :: Type
type Time = Double
type Signal :: Type -> Type
newtype Signal a = Sig { unSig :: Time -> a }
需要告诉我,我们需要newtype
作为实例,而Signal ..
只是封装了Time -> ..
。