不寻常的模棱两可的错误

时间:2014-11-20 21:24:08

标签: haskell

在下面的代码(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")

1 个答案:

答案 0 :(得分:2)

您看到错误,因为Show (DataOrError a b)的派生实例看起来像

instance (Show a, Show b) => Show (DataOrError a b) where
    ...

请注意,ab必须ShowDataOrError个实例才能拥有其实例。 x1x2的类型为DataOrError a Inte1e2的类型为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,我最初误解为错误,因为我总是将警告编译为错误。