As I understand it, Haskell's undefined
value - of a particular type - is a value that can't be determined, for whatever reason (maybe there is no sensible definition, or the calculation diverges). For instance: undefined :: [Int]
is a list, so it must be constructed with either [] or (:), but it doesn't know which one! Hence it makes sense that case splitting on undefined makes the whole expression undefined: I don't know whether null (undefined :: [a])
is True or False, because I don't know whether undefined :: [a]
is empty or not.
(By the way - if you disagree with my suggestion that undefined
is built with a constructor, then surely null (undefined :: [a])
should evaluate to False? After all undefined
isn't equivalent to []!)
However, Haskell pattern matching doesn't follow this way of thinking.
data Foo = Foo Int String -- Only one data constructor
silly :: Foo -> Bool
silly (Foo _ _) = True
ghci> silly (undefined :: Foo)
*** Exception: Prelude.undefined -- But whatever the value is, it must
-- be constructed with Foo.
-- So why fail to match?
(I know that newtype behaves differently here.)
Also:
foo :: Int -> String -> Bool
foo 8 "Hello" = True
foo _ _ = False
ghci> foo undefined undefined
*** Exception: Prelude.undefined -- GOOD - can't tell which case to choose.
ghci> foo undefined "Hello"
*** Exception: Prelude.undefined -- GOOD - still can't tell.
ghci> foo undefined "Goodbye"
*** Exception: Prelude.undefined -- BAD - should return false!
-- Pattern match on first line should fail,
-- because whatever the int is, the
-- string can't match the given pattern.
I think the rules on pattern matching currently say that if a sub-pattern fails then the pattern should immediately fail; if it diverges, then the whole pattern should also diverge; and if it succeeds, then the next sub-pattern should be tried. If all sub-patterns succeed, then the whole pattern succeeds.
What I am suggesting is that if any of the sub-patterns fail then the whole pattern should fail; if none fail but some diverge then the whole pattern should diverge; and if all succeed then the whole pattern should succeed.
Why is this not how Haskell does things?
EDIT:
So to summarise my interpretation of the responses I've read:
undefined
should be read as "This would cause your program to run forever" not "This is not well defined", and Exception: Prelude.undefined
should be read as "Watch out! Your program would have never terminated if undefined was an infinite loop" rather than "I don't know what to do because I don't know what value undefined is".
Could somebody please verify that this is correct? If so then I will probably accept mb14's answer.
Thanks everyone. Sorry for being so slow on the uptake!
undefined = error "Prelude.undefined"
, anderror
is basically raising an exception:error s = throw (errorCallException s)
. So it's an exception, hidden in the darks, getting thrown when you try to look into the value. And when you usefoo undefined "Goodbye"
, you're actually usingfoo (undefined :: Int) "Goodbye"
, so the compiler still generates code that checks the value ofundefined
. – Zeta