请向我解释单态限制吗?

use*_*402 8 haskell monomorphism-restriction

我开始做99个haskell问题,我遇到问题7,我的单元测试爆炸了.

显然,这是由于:http://www.haskell.org/haskellwiki/Monomorphism_restriction

我只是想确保我理解正确,因为我有点困惑.

情况1:func a定义为没有类型def或非严格类型def然后使用一次,编译器在编译时没有问题推断类型.

情况2:相同FUNC a在程序中使用多次,编译器不能100%确定是什么类型,除非它重新计算对于给定参数的函数.

为了避免计算损失,ghc向程序员抱怨它需要严格的类型def a 才能正常工作.

我想在我的情况下,assertEqual有类型def

 assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion
Run Code Online (Sandbox Code Playgroud)

test3我被定义为我解释为它有两种可能的类型testcase3(Show和Eq)并且不知道如何继续时,我得到了一个错误.

这听起来是否正确还是完全关闭?

problem7.hs:

-- # Problem 7
-- Flatten a nested list structure.

import Test.HUnit

-- Solution

data NestedList a = Elem a | List [NestedList a]

flatten :: NestedList a -> [a]
flatten (Elem x) = [x]
flatten (List x) = concatMap flatten x

-- Tests

testcase1 = flatten (Elem 5)
assertion1 = [5]

testcase2 = flatten (List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]])
assertion2 = [1,2,3,4,5]

-- This explodes
-- testcase3 = flatten (List [])

-- so does this:
-- testcase3' = flatten (List []) :: Eq a => [a]

-- this does not
testcase3'' = flatten (List []) :: Num a => [a]

-- type def based off `:t assertEqual`
assertEmptyList :: (Eq a, Show a) => String -> [a] -> Assertion
assertEmptyList str xs = assertEqual str xs []

test1 = TestCase $ assertEqual "" testcase1 assertion1
test2 = TestCase $ assertEqual "" testcase2 assertion2
test3 = TestCase $ assertEmptyList "" testcase3''

tests = TestList [test1, test2, test3]

-- Main
main = runTestTT tests
Run Code Online (Sandbox Code Playgroud)

第一种情况: testcase3 = flatten (List [])

GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main             ( problem7.hs, interpreted )

problem7.hs:29:20:
    Ambiguous type variable `a0' in the constraints:
      (Eq a0)
        arising from a use of `assertEmptyList' at problem7.hs:29:20-34
      (Show a0)
        arising from a use of `assertEmptyList' at problem7.hs:29:20-34
    Probable fix: add a type signature that fixes these type variable(s)
    In the second argument of `($)', namely
      `assertEmptyList "" testcase3'
    In the expression: TestCase $ assertEmptyList "" testcase3
    In an equation for `test3':
        test3 = TestCase $ assertEmptyList "" testcase3
Failed, modules loaded: none.
Prelude> 
Run Code Online (Sandbox Code Playgroud)

第二种情况: testcase3 = flatten (List []) :: Eq a => [a]

GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
[1 of 1] Compiling Main             ( problem7.hs, interpreted )

problem7.hs:22:13:
    Ambiguous type variable `a0' in the constraints:
      (Eq a0)
        arising from an expression type signature at problem7.hs:22:13-44
      (Show a0)
        arising from a use of `assertEmptyList' at problem7.hs:29:20-34
    Possible cause: the monomorphism restriction applied to the following:
      testcase3 :: [a0] (bound at problem7.hs:22:1)
    Probable fix: give these definition(s) an explicit type signature
                  or use -XNoMonomorphismRestriction
    In the expression: flatten (List []) :: Eq a => [a]
    In an equation for `testcase3':
        testcase3 = flatten (List []) :: Eq a => [a]
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)

Dan*_*her 4

与其说是单态性限制,不如说是通过默认解决不明确的类型变量导致了编译失败。

-- This explodes
-- testcase3 = flatten (List [])

-- so does this:
-- testcase3' = flatten (List []) :: Eq a => [a]

-- this does not
testcase3'' = flatten (List []) :: Num a => [a]

flatten :: NestedList a -> [a]
flatten (Elem x) = [x]
flatten (List x) = concatMap flatten x
Run Code Online (Sandbox Code Playgroud)

flatten对类型变量没有施加任何约束,因此这样的a定义没有问题,它将是多态的。testcase3

但当你使用它时test3

test3 = TestCase $ assertEmptyList "" testcase3 -- ''
Run Code Online (Sandbox Code Playgroud)

你继承了以下约束

assertEmptyList :: (Eq a, Show a) => String -> [a] -> Assertion
Run Code Online (Sandbox Code Playgroud)

现在编译器必须找出testcase3应该使用哪种类型。没有足够的上下文来确定类型,因此编译器尝试默认解析类型变量。根据默认规则,上下文(Eq a, Show a)不能通过默认来解决,因为只有涉及至少一个数字类的上下文才有资格进行默认。因此,由于类型变量不明确,编译失败。

testcase3'然而testcase3'',由于表达式类型签名对定义的右侧施加了由左侧继承的约束,因此受到单态性限制。

testcase3'因此,无论是否在断言中使用它,都无法编译。

testcase3''[Integer]由于表达式类型签名施加了数字约束,因此被默认为。因此,当 的类型单态化时testcase'',受约束类型变量默认为Integer。那么就不存在它在 中使用的类型的问题了test3

如果您已为绑定而不是右侧提供了类型签名,

testcase3' :: Eq a => [a]
testcase3' = flatten (List [])

testcase3'' :: Num a => [a]
testcase3'' = flatten (List [])
Run Code Online (Sandbox Code Playgroud)

这两个值都会自行编译为多态值,但仍然只能testcase3''在 中使用test3,因为只有这样才引入了允许默认所需的数字约束。