I have to learn Haskell for university and therefor I'm using learnyouahaskell.com for the beginning.
I always used imperative languages so I decided to practice Haskell by coding a lot more than I would for other languages.
I started to implement several functions to work with lists such as head
, tail
, init
,...
At some point I looked up the implementations of these functions to compare to mine and I stumbled upon the null function defined in List.lhs
.
null's implementation:
-- | Test whether a list is empty.
null :: [a] -> Bool
null [] = True
null (_:_) = False
my implementation:
mNull :: [a] -> Bool
mNull [] = True
mNull _ = False
I know there are no stupid questions even for such simple questions :)
So my question is why the original implementation uses (_:_)
instead of just _
?
Is there any advantage in using (_:_)
or are there any edge cases I don't know of?
I can't really imagine any advantage because _
catches everything.
Returns a Boolean value that indicates whether an expression contains no valid data (Null). The required expressionargument is a Variant containing a numeric expression or string expression. IsNull returns True if expression is Null; otherwise, IsNull returns False.
The R function is. null indicates whether a data object is of the data type NULL (i.e. a missing value). The function returns TRUE in case of a NULL object and FALSE in case that the data object is not NULL.
Null functions are nothing but a way to assign value to the pointer variable in c++. We can also do dereferencing of our null pointers in c++, but this will lead to unusual behavior of the program.
The C and C++ languages have a null character (NUL), a null pointer (NULL), and a null statement (just a semicolon (;)). The C NUL is a single character that compares equal to 0. The C NULL is a special reserved pointer value that does not point to any valid data object.
You can't just turn the clauses around with your solution:
mNull' :: [a] -> Bool mNull' _ = False mNull' [] = True
this will always yield False
, even if you pass an empty list. Because the runtime doesn't ever consider the []
clause, it immediately sees _
matches anything. (GHC will warn you about such an overlapping pattern.)
On the other hand,
null' :: [a] -> Bool null' (_:_) = False null' [] = True
still works correctly, because (_:_)
fails to match the particular case of an empty list.
That in itself doesn't really give the two explicit clauses an advantage. However, in more complicated code, writing out all the mutually excluse options has one benefit: if you've forgotten one option, the compiler can also warn you about that! Whereas a _
can and will just handle any case not dealt with by the previous clauses, even if that's not actually correct.
Because _
literally means anything apart from explicitly specified patterns. When you specify (_:_)
it means anything which can be represented as a list containing at least 1 element, without bothering with what or even how many elements the list actually contains. Since the case with an explicit pattern for empty list is already present, (_:_)
might as well be replaced by _
.
However, representing it as (_:_)
gives you the flexibility to not even explicitly pass the empty list pattern. In fact, this will work:
-- | Test whether a list is empty.
null :: [a] -> Bool
null (_:_) = False
null _ = True
Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With