首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >能给我解释一下单态限制吗?

能给我解释一下单态限制吗?
EN

Stack Overflow用户
提问于 2012-07-30 03:14:30
回答 1查看 725关注 0票数 8

我开始做99个haskell问题,我在problem 7上,我的单元测试失败了。

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

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

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

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

为了避免计算损失,ghc向程序员抱怨说,它需要在a上定义严格的类型才能正常工作。

我认为在我的情况下,assertEqual的类型定义为

代码语言:javascript
复制
 assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion

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

这听起来对吗?还是我完全错了?

problem7.hs:

代码语言:javascript
复制
-- # 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

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

代码语言:javascript
复制
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> 

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

代码语言:javascript
复制
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.
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-30 03:42:44

与其说是单态限制,不如说是defaulting对不明确类型变量的解析导致了编译失败。

代码语言:javascript
复制
-- 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

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

但是当你在test3中使用它时,

代码语言:javascript
复制
test3 = TestCase $ assertEmptyList "" testcase3 -- ''

继承了的约束

代码语言:javascript
复制
assertEmptyList :: (Eq a, Show a) => String -> [a] -> Assertion

现在编译器必须找出在那里应该使用哪种类型的testcase3。没有足够的上下文来确定类型,因此编译器尝试通过缺省来解析类型变量。根据defaulting rules,上下文(Eq a, Show a)不能通过缺省来解析,因为只有包含至少一个数字类的上下文才有资格缺省。因此,由于类型变量不明确,编译失败。

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

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

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

如果将类型签名提供给绑定,而不是提供给右侧,

代码语言:javascript
复制
testcase3' :: Eq a => [a]
testcase3' = flatten (List [])

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

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

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11712157

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档