简体   繁体   中英

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

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?

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] == []

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] where
    []     == []      =  True
    (x:xs) == (y:ys)  =    &&  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.

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM