You can only pattern match on data constructors. This is made confusing because the List type in Haskell relies on some syntatic sugar. So let's remove that and define our own List type so it's easier to see what's going on.
data List a = Nil | Cons a (List a)
Now when you want to declare a function that uses this List type, you can pattern match on the constructors.
myHead :: List a -> Maybe a
myHead Nil = Nothing
myHead (Cons a as) = Just a
This is the only kind of pattern matching you can do by default. List in Haskell is implemented with some sugar that renames Cons to (:)
and Nil to []
. And it let's you write your lists in [a, b, c]
notation, but fundamentally, these things are the same.
(++)
on the other hand is a normal function and not a data constructor.
My guess on what you're trying to do is look at the first few elements in a list and separate them from the rest of the list. You can do this in a pretty straightforward way using normal pattern matching.
getFirstThree :: [a] -> Maybe (a, a, a)
getFirstThree (a1:a2:a3:as) = Just (a1, a2, a3)
getFirstThree _ = Nothing
If you could pattern match on normal functions, this would be the same as writing:
getFirstThree :: [a] -> Maybe (a, a, a)
getFirstThree ([a1, a2, a3] ++ as) = Just (a1, a2, a3)
getFirstThree _ = Nothing
And maybe this second definition is more clear to you. I don't believe that this is the case for lists, but there are other data types where this might be true. For this sort of thing, the ViewPatterns
and PatternSynonyms
extensions exist and would let you define (++)
as a matchable pattern by basically telling the compiler how to perform the pattern match. However, as @Lee notes, this match is ambiguous because there are multiple valid matches for any given pattern that looks like (a ++ b)
.