gpt4 book ai didi

haskell - 请给我解释一下单态性限制?

转载 作者:行者123 更新时间:2023-12-04 08:36:39 24 4
gpt4 key购买 nike

我开始做 99 个 haskell 问题,我在 problem 7我的单元测试正在爆炸。

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

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

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

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

为了避免计算损失,ghc 向程序员提示它需要在 a 上的严格类型 def
正常工作。

我认为在我的情况下,assertEqual具有 def 类型

 assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion
test3 时出现错误被定义为我解释为它有 2 种可能的类型用于返回 testcase3 (Show and Eq) 并且不知道如何继续。

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

问题7.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

第一种情况: 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>

第二种情况: 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.

最佳答案

与其说是单态限制,不如说是通过defaulting 对歧义类型变量的解析。导致编译失败。

-- 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 中使用它时,
test3 = TestCase $ assertEmptyList "" testcase3 -- ''

你继承了约束
assertEmptyList :: (Eq a, Show a) => String -> [a] -> Assertion

现在编译器必须找出 testcase3 的类型。应该在那里使用。没有足够的上下文来确定类型,所以编译器尝试默认解析类型变量。根据 defaulting rules , 上下文 (Eq a, Show a)不能通过默认解决,因为只有涉及至少一个数字类的上下文才有资格默认。因此,由于类型变量不明确,编译失败。
testcase3'testcase3''然而,由于表达式类型签名在定义的右侧施加了由左侧继承的约束,因此属于单态限制。
testcase3'因此无法编译,无论它是否在断言中使用。
testcase3''默认为 [Integer]因为表达式类型签名强加了数字约束。因此,当类型为 testcase'' 单态时, 约束类型变量默认为 Integer .那么在 test3 中使用它的类型就没有问题了。 .

如果您为绑定(bind)而不是右侧提供了类型签名,
testcase3' :: Eq a => [a]
testcase3' = flatten (List [])

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

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

关于haskell - 请给我解释一下单态性限制?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11712157/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com