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

时间:2015-01-27 14:19:37

标签: haskell

对于错误消息有点沮丧,有些前奏函数(比如!!)给出了,我试着写一个不同的版本。

--(!!!) :: (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]

我不明白为什么第一个参数被推断为Strings列表而不仅仅是show实例列表。有人可以解释一下吗?

2 个答案:

答案 0 :(得分:8)

在最后一行中需要一些括号(或$):

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

现在将其解析为

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

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

答案 1 :(得分:6)

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

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

由于您的a类型的值与Stringerror "!!!: list " ++ show as)相连,因此a必须为String,因此返回f的类型必须为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