我正在尝试构建一个简单的monad计算器,但由于某种原因它不会编译。
{-# LANGUAGE GADTs #-}
module Main where
data Result a = Value a | Undefined deriving (Show, Eq)
data Calculator x a where
Bind :: Calculator x a -> (a -> Calculator x b) -> Calculator x b
Return :: a -> Calculator x a
Add :: a -> a -> Calculator x a
Div :: a -> a -> Calculator x a
instance Monad (Calculator x) where
return = Return
(>>=) = Bind
calculate :: Calculator (Result Integer) Integer
calculate = do
value <- addr 1 2
divr 1 value
addr :: a -> a -> Calculator (Result a) a
addr a b = Add a b
divr :: a -> a -> Calculator (Result a) a
divr a b = Div a b
eval :: (Integral a, Num a, Eq a) => Calculator (Result a) a -> Result a
eval (Add a b) = Value (a + b)
eval (Div a b)
| b == 0 = Undefined
| otherwise = Value $ a `div` b
eval (Bind m f) =
case eval m of
Value v -> eval $ f v
_ -> undefined -- for now
我得到的错误就是这个
Main.hs:35:13:
Could not deduce (a1 ~ a)
from the context (Integral a, Num a, Eq a)
bound by the type signature for
eval :: (Integral a, Num a, Eq a) =>
Calculator (Result a) a -> Result a
at Main.hs:28:9-72
‘a1’ is a rigid type variable bound by
a pattern with constructor
Bind :: forall x b a.
Calculator x a -> (a -> Calculator x b) -> Calculator x b,
in an equation for ‘eval’
at Main.hs:34:7
‘a’ is a rigid type variable bound by
the type signature for
eval :: (Integral a, Num a, Eq a) =>
Calculator (Result a) a -> Result a
at Main.hs:28:9
Expected type: Calculator (Result a) a
Actual type: Calculator (Result a) a1
Relevant bindings include
f :: a1 -> Calculator (Result a) a (bound at Main.hs:34:14)
m :: Calculator (Result a) a1 (bound at Main.hs:34:12)
eval :: Calculator (Result a) a -> Result a (bound at Main.hs:29:1)
In the first argument of ‘eval’, namely ‘m’
In the expression: eval m
错误发生在第三行到最后一行。谁知道为什么?
我正在使用ghci 7.8.4
。我错过了什么?
答案 0 :(得分:4)
首先,您的约束需要被推送到Calculator
构造函数中。为了了解原因,让我们稍微重命名Bind
签名中的类型变量:
data Calculator x a where
Bind :: Calculator x b -> (b -> Calculator x a) -> Calculator x a
eval :: (Integral a, Num a, Eq a) => ...
eval (Bind m f) = ...
此处m
的类型为Calculator x b
,而a
的约束(即(Integral a, Num a, Eq a)
)不会对b
施加任何限制,因此您没有希望在eval
上递归调用m
。
这个很容易解决:
data Calculator x a where
Bind :: Calculator x a -> (a -> Calculator x b) -> Calculator x b
Return :: a -> Calculator x a
Add :: (Num a) => a -> a -> Calculator x a
Div :: (Num a, Eq a, Integral a) => a -> a -> Calculator x a
然后eval
不再需要a
上的约束,因为它将通过相关构造函数上的模式匹配来提供。
另一个问题源于x
的{{1}}类型参数。在
Calculator
请注意Bind :: Calculator x b -> (b -> Calculator x a) -> Calculator x a
的选择保持不变。因此,当您在x
上进行模式匹配时,您会得到
Bind m f :: Calculator (Result a) a
但是,m :: Calculator (Result a) b
f :: b -> Calculator (Result a) a
的类型只能实例化为eval
或Calculator (Result a) a -> Result a
,因此我们无法在Calculator (Result b) b -> Result b
上递归应用它。
解决方案是在仿函数而不是类型上参数化m
,因为我们可以输入Calculator
:
eval :: Calculator Result a -> Result a