Reputation: 792
I am reading through the "Starting Out" chapter of Learn You a Haskell for Great Good!. It says:
null
checks if a list is empty. If it is, it returnsTrue
, otherwise it returnsFalse
. Use this function instead ofxs == []
(if you have a list calledxs
)
I tried in ghci:
xs = [] -- and then,
xs == []
null xs
Both of them are True
.
I wonder what's the difference.
Should I use the null
function instead of == []
and why?
Upvotes: 62
Views: 13317
Reputation: 477190
There is a difference. In order to use x == []
, the type of the elements of the list should be a member of the Eq
typeclass. Indeed, checking the equality of two lists is defined by the instance declaration:
instance Eq a => Eq [a] where
[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
_ == _ = False
That means that you can not use x == []
if x
is for example a list of IO Int
s.
null :: [a] -> Bool
on the other hand, uses pattern matching. This is implemented as:
null :: [a] -> Bool null [] = True null (_:_) = False
So regardless what type the elements of the list are, it will always typecheck.
Upvotes: 53
Reputation: 16224
Also a simple function that filter all empty list would fail:
withoutEmpty = filter (== [])
and that would be:
withoutEmpty = filter null
notice that:
withoutEmpty ls = filter (==[]) ls
will work just fine, but the important point is that in some cases like the other one could fail.
Also look at @cole answer, it complements all the answers here, the typeclass Foldable has the null
function there to be implemented:
To see more info of Foldable here
Upvotes: 2
Reputation: 725
In addition to the good answers given so far, null
actually has type
null :: Foldable t => t a -> Bool
I don't know if you've gotten to typeclasses in LYAH, but the short of it is that null
can be used not just for lists, but for any data structure that implements null
.
This is to say that using null
on a Map
or a Set
is valid, too.
> null Map.empty
True
> null (Map.singleton 1)
False
> null Set.empty
True
> null (Set.singleton 1)
False
> null []
True
> null [1]
False
I don't think it's especially common to write functions that need to be this general, but it doesn't hurt to default to writing more general code.
In many cases, you'll end up wanting to use a function like null
to do conditional behavior on a list (or other data structure). If you already know that your input is a specific data structure, it's more elegant to just pattern match on its empty case.
Compare
myMap :: (a -> b) -> [a] -> [b]
myMap f xs
| null xs = []
myMap f (x:xs) = f x : myMap f xs
to
myMap' :: (a -> b) -> [a] -> [b]
myMap' f [] = []
myMap' f (x:xs) = f x : myMap' f xs
In general, you should try to prefer pattern matching if it makes sense.
Upvotes: 28
Reputation: 153102
You should use null
. In most cases it doesn't matter, but it is a good habit to get into anyway, because occasionally you may want to check if a list of non-comparable things is empty. Here is a short, crisp example showing this difference:
> null [id]
False
> [id] == []
<interactive>:1:1: error:
• No instance for (Eq (a0 -> a0)) arising from a use of ‘==’
(maybe you haven't applied a function to enough arguments?)
• In the expression: [id] == []
In an equation for ‘it’: it = [id] == []
Upvotes: 77