首页 文章

Haskell - 无法将预期类型'b'与实际类型'a'匹配

提问于
浏览
3

刚开始学习Haskell,我试图实现一个max函数来递归地查找列表的最大值

max' :: (Num b) => [a] -> b
max' [] = 0
max' (x:xs)
    | x > max' xs = x
    | otherwise = max' xs

但在尝试编译时遇到错误

无法将预期类型'b'与实际类型'a'匹配'a''是由类型签名绑定的刚性类型变量:max':: forall b a . (Num b,Num a)=> [a] - > b at implementationFunctions.hs:5:1-34'b'是由类型签名绑定的刚性类型变量:max':: forall b a . (Num b,Num a)=> [a] - > b at implementationFunctions.hs:5:1-34

任何人都可以帮我理解什么是错的?

2 回答

  • 1

    max' 作为输入(基于签名) [a]a 的列表 . 但是你返回 b . 这意味着你已经写了 - 无论列表中的元素类型如何 - 我们可以选择任何类型 b 作为我们想要的输出,只要它是 Num b . 但这没有意义 . 如果我们输入一个字符串列表,我们当然可以计算"largest string"(按字典顺序排列),但我们不能将其作为 Num 返回 .

    另一个问题是你使用 (>) :: Ord a => a -> a -> Bool 函数(作为一个守卫) . 但是,您没有在函数签名中指定输入元素的类型必须是 Ord 类型类的实例 . 所以你无法比较这些元素 .

    最小的修复是将输入类型限制为 b

    max' :: (Ord b, Num b) => [b] -> b
    max' [] = 0
    max' (x:xs)
        | x > max' xs = x
        | otherwise = max' xs
    

    话虽如此,如果我们提供一个空列表,返回 0 没有多大意义 . 这将导致 max [] 实际上大于 max [-1] 的奇怪事实:通常我们期望超集的最大值大于或等于集合的最大值 .

    因此, max' 函数可能最好被视为非总函数:不是每个输入都会导致输出的函数 . 在这种情况下,空列表不会 .

    我们可以将其重写为错误:

    max' :: Ord b => [b] -> b
    max' [] = error "Empty list"
    max' [x] = x
    max' (x:xs@(_:_))
        | x > max' xs = x
        | otherwise = max' xs
    

    所以现在有三种模式:(1)空列表,(2)单元列表,以及(3)具有至少两个元素的列表 .

    然而,编写错误并不总是处理非总函数的好方法,因为在类型签名中看不到函数是非总函数 . 另一个想做的就是使用 Maybe b 作为返回类型 . 如果没有最大值,这将是 Nothing ,如果有最大值,则为 Just x

    max' :: Ord b => [b] -> Maybe b
    max' [] = Nothing
    max' [x] = Just x
    max' (x:xs@(_:_))
        | y <- max' xs = max x y
        | otherwise = Nothing
    

    或更短:

    max' :: Ord b => [b] -> Maybe b
    max' [] = Nothing
    max' [x] = Just x
    max' (x:xs@(_:_)) = fmap (max x) (max' xs)
    

    例如:

    Prelude> max' []
    Nothing
    Prelude> max' [1,4,2,5]
    Just 5
    Prelude> max' [-3]
    Just (-3)
    
  • 10

    你的函数会获取一些东西并返回其中一个东西 . 但函数的类型签名表示它需要一个 [a] 的列表并返回一个完全不同的东西 b . 这困扰了编译器 . 它无法将声明的类型签名与实际实现(也就是"typechecking")进行协调 .

    要解决此问题,请使类型签名与实现匹配:

    max' :: (Num a) => [a] -> a
    

相关问题