Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a null value in Haskell without constricting Polymorphism

Tags:

haskell

I'm following "Real World Haskell", and am going through chapter 2, where the exercise is to create a "lastButOne" function which returns the second to last value in the list. My original code was:

lastButOne xs =     if null (tail (tail xs))
                    then head xs
                    else lastButOne (tail xs)

Which works fine unless I give it a list with only 1 or 2 items. So I modified it to this:

lastButOne xs = if null xs || null (tail xs)
                then head xs
                else
                        if null (tail (tail xs))
                        then head xs
                        else lastButOne (tail xs)

Which checks if it only has 1 or 2 items, but then fails if it only has 1 item because of the call to head. My problem is that I can't think of anything else to return other than "head xs", ideally I want it to return null or something like that, but I can't find a "null" that allows the function to still be polymorphic.

like image 457
Peter Avatar asked Nov 10 '11 10:11

Peter


People also ask

How do I return a null in Haskell?

However, Haskell doesn't have a null that can replace the Char as the return type. Therefore, it's common practice to use Maybe instead. The new type signature would then be. This would mean that upon finding the value v, you would yield "Just v" and in the case where you don't, it would yield "Nothing".

Can a Haskell function return nothing?

No. However, you can have functions that return a trivial value. The () type has only one inhabitant, () .

How do I use null in Haskell?

Haskell does not have "null". This is a design feature. It completely prevents any possibility of your code crashing due to a null-pointer exception. If you look at code written in an imperative language, 99% of the code expects stuff to never be null, and will malfunction catastrophically if you give it null.

What does nil mean in Haskell?

The Nil constructor is an empty list. It contains no objects. So any time you're using the [] expression, you're actually using Nil . Then the second constructor concatenates a single element with another list.


2 Answers

Perhaps you are looking for a Maybe type:

lastButOne :: [a] -> Maybe a
lastButOne [x,_] = Just x
lastButOne (_:y:ys) = lastButOne (y:ys)
lastButOne _ = Nothing
like image 167
Matvey Aksenov Avatar answered Oct 15 '22 12:10

Matvey Aksenov


[I]deally I want it to return null or something like that, but I can't find a "null" that allows the function to still be polymorphic.

There are a number of answers already in posted on how to make this work, so instead let me answer the more fundamental question of "Why isn't there a 'null'?"

This is part of the philosophy of the type-checker in Haskell. most languages include some form of a special value that signals an error or lack of data. Examples include 0,-1 "" (the empty string) JavaScripts null or Ruby's nil. The problem is that this creates a whole class of potential bugs wherein the calling function only deals with "good" values and the program crashes or worse corrupts data when a special value is returned. There is also the issue of the special value being intended as a regular value -- that is 0 actually being a number and not a failure state.

In your problem, the question could be framed, "Would the function that calls lastButOne know what to do with null?"

The problem of not having a valid answer for certain otherwise well-formed inputs is a very common one in Haskell and has two typical solutions. The first is taken from Erlang: Fail quickly and cleanly. Under this solution it is the responsibility of the caller to ensure that the data is "good." If it is not the program dies with an error (or the exception is caught, but this is more difficult in Haskell then other languages) This is the solution used by most list functions e.g. head, tail, etc...

The other solution is to explicitly state that the function may fail to produce good data. The Maybe a type is the simplest of these solutions. It allows you to return a value Nothing that works much like the null you are looking for. But there is a cost to this. Your function's type signature becomes lastButOne :: [a] -> Maybe a. Now every calling function must take not an a but a Maybe a, and before it can get its answer, it must tell Haskell what it is going to do if the answer is Nothing.

like image 37
John F. Miller Avatar answered Oct 15 '22 11:10

John F. Miller