Before you wrote that, did you try writing getSymbol Nothing = Nothing
? What you wrote works, for sure, but its like writing
f :: Bool -> Int
f True = 1
f isFalse = 0
Yes, that works, but can you see why it’s kind of silly? There is only one other data constructor in the Bool
type, namely False
, and so we know that the only other argument to the function that the compiler will accept is False
, so why wouldn’t we use the constructor itself to pattern match on, instead of providing a defaulting case which is what putting a variable like isNothing
or isFalse
does? It’s the same with your function, the only other possible input to your function that Haskell will accept is Nothing
, so why wouldn’t you just pattern match on it?
I was being genuine when I said that if you are confused about what is going on, you should write down all the possible values of type Maybe Operations
, (there are only six, it’s not a big list) and I still think that it will help you clear up what is going on. When you pattern match on a type, and this is why I referenced the disjointedness of sum types, you need to provide a case for each constructor, or else have a defaulting case. Maybe
is defined like this
data Maybe a = Nothing | Just a
which means that it has two constructors, Nothing
and Just a
. It doesn’t matter what type a
is in Maybe a
, if you want to write a function to pattern match on Maybe a
, you need to write your function like
g :: Maybe a -> b
g (Just a) = doSomething
g Nothing = doSomethingElse
Otherwise you are missing a pattern (unless you provide a default, but again I outlined above why a default seems weird in this kind of situation). It doesn’t matter what doSomething
does, it doesn’t matter if it itself is a case analysis on the type a
that could even involves its own Maybe
type, you are passing a value of type Maybe a
into the function, so the only argument that will cause doSomething
to be evaluated is one of the form Just a
. Nothing
is not of that form, it is a different type constructor, and so Haskell will not enter into the body of the Just a
pattern when given Nothing
because it does not match that pattern. There is no way for the guards to ‘catch’ the Nothing
because Nothing
is a pattern that is matched against the definition of your function getSymbol
, not against a function on the other side of the equals sign, which in essence is what the guards are (if you don’t see how that is, rewrite your guards using nested if then else
’s, for example).
And here, I’ll write out all six of the values in case you really don’t want to do it:
(Just Addition) :: Maybe Operations
(Just Subtraction) :: Maybe Operations
(Just Multiplication) :: Maybe Operations
(Just Division) :: Maybe Operations
(Just Modulus) :: Maybe Operations
Nothing :: Maybe Operations
No other value (other than something like undefined
obviously) has the type Maybe Operations
. If you want to pattern match on the type, you need to provide a case for each of the constructors, Just a
and Nothing
, and writing only one case that matches on Just a
will not cover the Nothing
case, because it is a different pattern, and so the compiler will throw a non-exhaustive pattern error.