此代码将正确编译:
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
答案 0 :(得分:6)
这是单态限制的一个例子。 ae
“看起来像一个值”(没有参数)并且没有显式类型,因此编译器不会为它推断出多态类型。
在第一个示例中,它获取类型Int -> Int -> Assertion
(我认为)。
在第二个中,从ae "It's a-me, Conan O'Brien!" conanO'Brien
获取类型String -> String -> Assertion
。请记住,整数文字的类型实际上是Num a => a
,1000
从String
获取类型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"
允许参数多态(并且没有性能开销)!