为什么我的(替代!!)函数有这种类型

why does my (alternative to !!) function have this type

对某些前奏函数(如 !!)给出的错误消息感到有点沮丧,我尝试编写一个不同的版本。

--(!!!) :: (Show a,Integral b)=> [a]->b->a
as !!! y=f as y
    where f (x:xs) b= if b==0
                      then x
                      else f xs (b-1)
          f [] _= error "!!!: list "++(show as)++" has less than "++show y++" elements"

但是函数类型是

*Handydandy> :type (!!!)
(!!!) :: (Show a, Num a, Eq a) => [[Char]] -> a -> [Char]

我不明白为什么这里的第一个参数被推断为一个字符串列表,而不是一个 show 实例列表。有人能解释一下吗?

你需要在最后一行加上括号(或 $):

error ("!!!: list "++(show as)++" has less than "++show y++" elements")

现在它被解析为

(error "!!!: list ") ++ (show as) ++ " has less than " ++ show y ++" elements"

这使得 Haskell 认为 (!!!) 正在返回一个字符串,这意味着它的输入必须是一个字符串列表。

这是因为您使用了errorerror 的类型是 String -> a,而你键入它的方式编译器将其视为

f [] _ = (error "!!!: list ") ++ (show as) ++ " has less than " ++ (show y) ++ " elements"

由于您有一个 a 类型的值与 String 连接(即 error "!!!: list " ++ show as),那么 a 必须是 String,因此f 的 return 类型必须是 String。您有 f (x:xs) 0 = x,因此 x 必须具有类型 String,因此 xs :: [String].

您可以使用 $ 解决此问题:

f [] _ = error $ "!!!: list " ++ show as ++ " has less than " ++ show y ++ " elements"

现在这个函数的类型是(!!!) :: (Show t, Show a, Num a, Eq a) => [t] -> a -> t,更像你想要的。

但是,如果您想要一种更好的方法来在代码中处理此问题,而不仅仅是在运行时错误消息中,您可以使用 Maybe 数据类型,如果需要,甚至可以使用 Either 类型更多信息:

(!!?) :: [a] -> Int -> Maybe a
[]     !!? _ = Nothing
(x:_)  !!? 0 = Just x
(_:xs) !!? n = xs !!? (n - 1)

(!!^?) :: [a] -> Int -> Either ([a], Int) a
ys !!^? m = go ys m
    where
        go []     _ = Left (ys, m)
        go (x:_)  0 = Right x
        go (_:xs) n = go xs $ n - 1

那么你可以

showIndexError :: Show a => Either ([a], Int) a -> String
showIndexError (Left (xs, n)) = "!!^?: list " ++ show xs ++ " has less than " ++ show y ++ " elements"
showIndexError (Right x) = show x