在 Haskell 中,您不能真正以与在 Scheme 中相同的方式来考虑嵌套列表,因为它们不是相同的数据结构。Haskell 列表是同质的,而 Lisp“列表”实际上更接近于玫瑰树(如下面的 CAMcCann 所指出的)。作为一个说明性示例,看一下WYAS48 解析部分如何定义LispVal
.
如果你真的,真的,真的想进行运行时类型检查,即使这通常是一个坏主意并且在 Haskell 中非常不合常规,请查看Data.Typeable。此响应也可能有用。
这个问题的真正答案是“你需要在 Haskell 和 Lisp 中以不同的方式思考你的论点,这导致永远不需要在运行时自己执行这个检查”(我说这是一个 Common Lisper,所以我理解这是多么令人沮丧那就是开始)。
附录:响应您的编辑,Haskell 的类型系统自动确保这一点。例如,如果你有一个 type 的函数foo :: [Int] -> Int
,并且你传递它["One", "Two", "Three"]
or [[1, 2, 3]]
,你会得到一个编译时错误,告诉你刚刚发生了什么以及为什么发生了爆炸。如果你想专门化一个函数,只需声明一个更具体的类型。
例如(不要写这样的代码,它只是为了说明目的),假设你有一个简单的函数,比如
myLookup index map = lookup index map
如果你将它加载到 GHCi 并运行:t myLookup
,它会告诉你函数的类型是myLookup :: Eq a => a -> [(a, b)] -> Maybe b
,这意味着它可以采用任何派生类型的键Eq
(任何你可以运行==
的东西)。现在,无论出于何种原因,您都想确保只使用数字作为键。您可以通过添加更具体的类型声明来确保
myLookup :: Int -> [(Int, a)] -> Maybe a
myLookup index map = lookup index map
现在,即使函数体中没有任何内容阻止它处理其他键类型,但如果您尝试将Int
索引或[(Int, a)]
映射以外的其他内容传递给它,您将在编译时收到类型错误。结果,这
myLookup :: Int -> [(Int, a)] -> Maybe a
myLookup ix lst = lookup ix lst
main :: IO ()
main = putStrLn . show $ myLookup 1 [(1, "Foo")]
将编译并运行良好,但这
myLookup :: Int -> [(Int, a)] -> Maybe a
myLookup ix lst = lookup ix lst
main :: IO ()
main = putStrLn . show $ myLookup "Nope.jpg" [("Foo", 1)]
两者都不会。在我的机器上,它在编译时出错
/home/inaimathi/test.hs:5:35:
Couldn't match expected type `Int' with actual type `[Char]'
In the first argument of `myLookup', namely `"Nope.jpg"'
In the second argument of `($)', namely
`myLookup "Nope.jpg" [("Foo", 1)]'
In the expression:
putStrLn . show $ myLookup "Nope.jpg" [("Foo", 1)]
Failed, modules loaded: none.
我真的希望这不会让你更困惑。