在下面的代码(ideone link)中,为什么第(1)行在没有问题(ideone link with line (1) commented)的情况下编译时失败:
data DataOrError a b = Error a | Data b deriving Show
apply f (Data x) (Data y) = Data (f x y)
apply f (Data x) y@(Error _) = y
apply f x@(Error _) _ = x
main = do
print (apply (+) x1 x2) -- (1)
print (apply (+) x1 e2) -- (2)
print (apply (+) e1 x2) -- (3)
print (apply (+) e1 e2) -- (4)
where
x1 = Data (2 :: Int)
x2 = Data (3 :: Int)
e1 = Error ("First thing failed")
e2 = Error ("Second thing failed")
我知道DataOrError
基本上是Either
,这只是为了说明。
错误是:
prog.hs:8:3:
No instance for (Show a0) arising from a use of `print'
The type variable `a0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance (Show a, Show b) => Show (DataOrError a b)
-- Defined at prog.hs:1:50
instance Show Double -- Defined in `GHC.Float'
instance Show Float -- Defined in `GHC.Float'
...plus 24 others
In a stmt of a 'do' block: print (apply (+) x1 x2)
In the expression:
do { print (apply (+) x1 x2);
print (apply (+) x1 e2);
print (apply (+) e1 x2);
print (apply (+) e1 e2) }
In an equation for `main':
main
= do { print (apply (+) x1 x2);
print (apply (+) x1 e2);
print (apply (+) e1 x2);
.... }
where
x1 = Data (2 :: Int)
x2 = Data (3 :: Int)
e1 = Error ("First thing failed")
e2 = Error ("Second thing failed")
答案 0 :(得分:2)
您看到错误,因为Show (DataOrError a b)
的派生实例看起来像
instance (Show a, Show b) => Show (DataOrError a b) where
...
请注意,a
和b
必须Show
个DataOrError
个实例才能拥有其实例。 x1
和x2
的类型为DataOrError a Int
,e1
和e2
的类型为DataOrError String b
。这意味着DataOrError
的其他类型变量不会限制为Show
。您可以使用显式类型参数来解决此问题:
main :: IO ()
main = do
print (apply (+) x1 x2) -- (1)
print (apply (+) x1 e2) -- (2)
print (apply (+) e1 x2) -- (3)
print (apply (+) e1 e2) -- (4)
where
x1 = Data 2 :: DataOrError String Int
x2 = Data 3 :: DataOrError String Int
e1 = Error "First thing failed" :: DataOrError String Int
e2 = Error "Second thing failed" :: DataOrError String Int
只要是()
的实例,您就可以为那些未使用的类型变量添加任何内容,包括Show
。发生这种情况的唯一原因是编译器试图提供帮助,并推断出您未指定的参数比您实际想要的更为通用。虽然你可以说它无关紧要,但是编译器不会查看值以确定是否可以打印某些内容,而是查看类型。
你没有在第4行看到错误而在第1行看到错误的原因是因为默认。对于第2行和第3行,它可以找出apply (+)
的返回值的完整类型,但在第4行,编译器只知道它必须是Num a
。然后选择将其默认为Integer
,我最初误解为错误,因为我总是将警告编译为错误。