空列表

时间:2018-03-17 16:12:28

标签: haskell empty-list

考虑下面的代码片段,它定义了一个函数foo,它接受​​一个列表并对列表执行某些操作(如排序)。 我尝试在ghci中加载代码段:

-- a function which consumes lists and produces lists
foo :: Ord a => [a] -> [a]
foo [] = []
foo (x:xs) = xs

test1 = foo [1, 2, 3] == [2, 3]
test2 = null $ foo []

但是发生以下错误:

No instance for (Ord a0) arising from a use of ‘foo’
    The type variable ‘a0’ is ambiguous
    Note: there are several potential instances:
      instance (Ord a, Ord b) => Ord (Either a b)
        -- Defined in ‘Data.Either’
      instance forall (k :: BOX) (s :: k). Ord (Data.Proxy.Proxy s)
        -- Defined in ‘Data.Proxy’
      instance (GHC.Arr.Ix i, Ord e) => Ord (GHC.Arr.Array i e)
        -- Defined in ‘GHC.Arr’
      ...plus 26 others
    In the second argument of ‘($)’, namely ‘foo []’
    In the expression: null $ foo []
    In an equation for ‘test2’: test2 = null $ foo []

问题在于test2 = null $ foo []。此外,从Ord a的类型定义中删除foo约束将解决问题。奇怪的是,在交互式模式下输入null $ foo [](在加载foo的定义之后)可以正常工作并生成预期的true

我需要清楚解释这种行为。

1 个答案:

答案 0 :(得分:4)

我喜欢在#34;字典传递风格"中考虑类型类。签名

foo :: Ord a => [a] -> [a]

表示fooOrd a提供方法字典,主要是作为参数和a的列表,并返回a的列表。字典里面有(<) :: a -> a -> Bool及其表兄弟。当我们调用foo时,我们需要提供这样的字典。这是由编译器隐式完成的。所以

foo [1,2,3]

将使用Ord Integer词典,因为我们知道aInteger

但是,在foo []中,列表可以是任何内容的列表 - 没有信息可以确定类型。但是我们仍然需要找到要传递给Ord的{​​{1}}字典(虽然您的foo根本没有使用它,但签名表示它可以,而且&#39}这一切都很重要。这就是为什么存在模糊类型错误的原因。您可以手动指定类型,这将提供足够的信息来填写字典,例如

foo

或使用新的null (foo ([] :: [Integer])) 扩展程序

TypeApplications

如果删除了null (foo @Integer []) 约束,它就可以正常工作,这只是因为我们不再需要提供字典了。我们不需要知道Ord将要调用a的具体类型(这对我来说有点神奇: - )。

请注意,foo 消除了歧义,因为不知道您要传递哪个特定的foo ([] :: Ord a => [a])字典;是Ord还是Ord Int等?有没有通用Ord (Maybe String)字典,所以我们必须选择,并且在这种情况下有 no 规则选择哪种类型。而当你说Ord时,默认指定了一种选择方式,我们选择(Ord a, Num a) => [a],因为它是Integer类的特例。

Numfoo []中有效的事实归因于ghci的{​​{3}}规则。总的来说,extended defaulting可能值得一读,这肯定不是Haskell中最漂亮的部分,但是在你要问的那些角落案例中会出现很多。