Why use null function instead of == [] to check for empty list in Haskell?

ListHaskellIs EmptyEmpty List

List Problem Overview


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 returns True, otherwise it returns False. Use this function instead of xs == [] (if you have a list called xs)

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?

List Solutions


Solution 1 - List

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 forit’: it = [id] == []

Solution 2 - List

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 Ints.

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.

Solution 3 - List

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.

A side note

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.

Solution 4 - List

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Question徐保钰View Question on Stackoverflow
Solution 1 - ListDaniel WagnerView Answer on Stackoverflow
Solution 2 - ListWillem Van OnsemView Answer on Stackoverflow
Solution 3 - ListcoleView Answer on Stackoverflow
Solution 4 - ListDamian LatteneroView Answer on Stackoverflow