奇怪的Haskell类型错误仅在curally assertEqual时发生

时间:2011-08-14 18:13:17

标签: haskell

此代码将正确编译:

import Text.Printf
import Test.HUnit
doubleMe x = x + x
doubleUs x y = doubleMe x + doubleMe y
doubleSmallNumber x = if x > 100 then x else x*2
doubleSmallNumber' x = if x > 100 then x else x*2 + 1
conanO'Brien = "It's a-me, Conan O'Brien!"
main = do
  runTestTT $ TestList [TestCase $ ae 4 $ doubleMe 2,
                        TestCase $ ae 10 $ doubleUs 2 3,
                        TestCase $ ae 4 $ doubleSmallNumber 2,
                        TestCase $ ae 1000 $ doubleSmallNumber' 1000,
                        TestCase $ assertEqual "" "It's a-me, Conan O'Brien!" conanO'Brien]
  where ae = assertEqual "" 

输出结果为:

$ clear && ghc baby.hs && ./baby     
[1 of 1] Compiling Main             ( baby.hs, baby.o )
Linking baby ...
ld: warning: could not create compact unwind for .LFB3: non-standard register 5 being saved in prolog
Cases: 5  Tried: 5  Errors: 0  Failures: 0

当我将代码更改为:

import Text.Printf
import Test.HUnit
doubleMe x = x + x
doubleUs x y = doubleMe x + doubleMe y
doubleSmallNumber x = if x > 100 then x else x*2
doubleSmallNumber' x = if x > 100 then x else x*2 + 1
conanO'Brien = "It's a-me, Conan O'Brien!"
main = do
  runTestTT $ TestList [TestCase $ ae 4 $ doubleMe 2,
                        TestCase $ ae 10 $ doubleUs 2 3,
                        TestCase $ ae 4 $ doubleSmallNumber 2,
                        TestCase $ ae 1000 $ doubleSmallNumber' 1000,
                        TestCase $ ae "It's a-me, Conan O'Brien!" conanO'Brien]
  where ae = assertEqual "" 

我明白了:

[1 of 1] Compiling Main             ( baby.hs, baby.o )

baby.hs:12:65:
    No instance for (Num [Char])
      arising from the literal `1000'
    Possible fix: add an instance declaration for (Num [Char])
    In the first argument of `doubleSmallNumber'', namely `1000'
    In the second argument of `($)', namely `doubleSmallNumber' 1000'
    In the second argument of `($)', namely
      `ae 1000 $ doubleSmallNumber' 1000'

我不明白为什么。

也有人对修复ld警告有任何想法:

ld: warning: could not create compact unwind for .LFB3: non-standard register 5 being saved in prolog

4 个答案:

答案 0 :(得分:6)

这是单态限制的一个例子。 ae“看起来像一个值”(没有参数)并且没有显式类型,因此编译器不会为它推断出多态类型。

在第一个示例中,它获取类型Int -> Int -> Assertion(我认为)。

在第二个中,从ae "It's a-me, Conan O'Brien!" conanO'Brien获取类型String -> String -> Assertion。请记住,整数文字的类型实际上是Num a => a1000String获取类型ae,因此编译器需要一个实例Num String

已编辑:可以通过提供显式类型注释来解决此问题:where ae :: (Show a, Eq a) => a -> a -> Assertion = assertEqual ""。或者通过向定义添加参数(eta-expansion it):where ae x y = assertEqual "" x y

答案 1 :(得分:6)

这是单态限制。 ae函数被赋予单态类型。要么关闭限制,要么给ae一个类型签名。

答案 2 :(得分:4)

@augustss和@Alexey Romanov是对的。如果将ae移动到顶层并删除最后一个断言,则可以看到*Main> :t ae ae :: Integer -> Integer -> Assertion 的推断类型:

ae

如果你在where子句中保留main = do runTestTT $ TestList [TestCase $ ae 4 $ doubleMe 2, TestCase $ ae 10 $ doubleUs 2 3, TestCase $ ae 4 $ doubleSmallNumber 2, TestCase $ ae 1000 $ doubleSmallNumber' 1000, TestCase $ ae "It's a-me, Conan O'Brien!" conanO'Brien] where ae :: (Show a, Eq a) => a -> a -> Assertion ae = assertEqual "" ,但添加一个更通用类型的类型签名,它将起作用:

{{1}}

答案 3 :(得分:3)

只是添加一个脚注,它是重载,这是由单态限制排除的。所以,这没关系

foo :: String -> b -> b
foo _ b = b

goo :: (Int, Bool)
goo = (moo 2, moo True) where moo = foo "boo"

不是

hoo :: Eq b => String -> b -> b -> Bool
hoo _ b c = b == c

ioo :: (Bool, Bool)
ioo = (moo 2 2, moo True True) where moo = hoo "boo"

允许参数多态(并且没有性能开销)!