You cannot think of nested lists in the same way in Haskell as in Scheme, because this is not the same data structure. Haskell's list is homogeneous, where as a Lisp, the "list" is actually closer to the rosewood (as indicated in CAMcCann below). As an illustrative example, consider how the parsing section WYAS48 defines LispVal .
If you really, really, really want to do runtime type checking, although this is usually a bad idea and very unconventional in Haskell, see Data.Typeable . This answer may also be helpful.
The real answer to this question is: “You need to think differently about your arguments in Haskell than in Lisp, which means you never need to run this check yourself at runtime” (and I say this as Common Lisper, therefore I understand how to upset this to start with).
Addition . In response to your editing, a system like Haskell automatically provides this. For example, if you have a function like foo :: [Int] -> Int and you pass it ["One", "Two", "Three"] or [[1, 2, 3]] , you will get a time error a compilation telling you what just exploded and why. If you want to specialize a function, just declare a more specific type.
For example (do not write such code, this is just for illustrative purposes), let's say you have a simple function, like
myLookup index map = lookup index map
If you load this into GHCi and run :t myLookup , it will tell you that the function type is myLookup :: Eq a => a -> [(a, b)] -> Maybe b means that it can take a key of any type, which outputs Eq (all you can run == on). Now say that for some reason you want you to only use numbers as keys. You need to make sure that adding a more specific declaration like
myLookup :: Int -> [(Int, a)] -> Maybe a myLookup index map = lookup index map
Now, although there is nothing in the function body that would prevent it from dealing with other types of keys, you will get a type error at compile time if you try to pass it something other than the Int index or something other than the map [(Int, a)] . As a result
myLookup :: Int -> [(Int, a)] -> Maybe a myLookup ix lst = lookup ix lst main :: IO () main = putStrLn . show $ myLookup 1 [(1, "Foo")]
will compile and work fine, but it
myLookup :: Int -> [(Int, a)] -> Maybe a myLookup ix lst = lookup ix lst main :: IO () main = putStrLn . show $ myLookup "Nope.jpg" [("Foo", 1)]
will not. On my machine, these are errors during compilation with
/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.
I really hope this didn’t bother you anymore.