[Haskell-cafe] handling NULL value in database query with Maybe (or other ...)

Damien Mattei mattei at oca.eu
Wed Dec 19 16:14:47 UTC 2018


thank tou for your C/HASKELL "rosetta stone" about the Monad it helps a
lot understanding the thing...

i always thougt Monads are powerful but they are not easy sot
manipulates syntactically for a beginner in Haskell.

yes in fact what i secretly expected  was a way to discard NULL fields
automagically and it seems really possible with Haskell.

Le 19/12/2018 11:27, Mario Lang a écrit :
> Damien Mattei <mattei at oca.eu> writes:
> 
>> using Nothing and Just compared to other language, it's the same than
>> if i had written in C something like :
>> if (x == NULL) printf("NULL\n");
>> else printf("%s",*x);
>>
>> i expected more from Haskell...
> 
> Haskell actually offers more.
> 
> 1. The type of "Maybe a" prevents you from ignoring the fact that "a" might
> be null.  In C, you can accidentally write
> 
> printf("%s",*x);
> 
> and risking a segfault.  The compiler will not care.  IN Haskell, the
> types dont even allow you to write something like that.  You would be
> forced to check for Just or Nothing in one of several ways.
> 
> Now, "Maybe a" doesn't only limit your abilities, it also offers you the
> functions from the Functor, Applicative and Monad typeclasses.
> 
> 2. Functor allows you to apply a function to the "a", as long as there is
> some "a" inside the "Maybe a".  So, "fmap f x" is roughly equivalent to:
> 
> typeOfA f(typeOfA);
> typeOfA *x func(typeOfA *x) {
>   if (x) {
>     typeOfA *val = malloc(sizeof(typeOfA));
>     *val = f(*x);
>     return val;
>   }
>   return NULL;
> }
> func(x);
> 
> (note that pure Haskell doesn't have assignment, so the translation to C
> is a little bit more verbose then it might be if you were using values
> on the stack.)

i tried this:

 let resFMAP = fmap (\(Only a) -> (putStrLn ("a =" ++ Text.unpack a)))
bd_rows_WDS

but compile complains now that i use a Maybe variable instead of a plain
one,strange because this exactly what i expected from Maybe use it to
automatically discard the Nothing values (avoiding to putStrln them):
Prelude> :load UpdateSidonie
[1 of 1] Compiling Main             ( UpdateSidonie.hs, interpreted )

UpdateSidonie.hs:293:75: error:
    • Couldn't match type ‘Maybe Text’ with ‘Text’
      Expected type: [Only Text]
        Actual type: [Only (Maybe Text)]
    • In the second argument of ‘fmap’, namely ‘bd_rows_WDS’
      In the expression:
        fmap (\ (Only a) -> (putStrLn ("a =" ++ unpack a))) bd_rows_WDS
      In an equation for ‘resFMAP’:
          resFMAP
            = fmap (\ (Only a) -> (putStrLn ("a =" ++ unpack a)))
bd_rows_WDS
    |
293 |     let resFMAP = fmap (\(Only a) -> (putStrLn ("a =" ++
Text.unpack a))) bd_rows_WDS
    |
     ^^^^^^^^^^^
Failed, no modules loaded.

bd_rows_WDS is defined like this with a Maybe:

(bd_rows_WDS :: [Only (Maybe Text)]) <- query conn qry_head_WDS (Only
(name::String))

anyway i know enought in haskell now to remove myself the Nothing (NULL)
values of the list doing that (it worked):

-- remove the records having N°BD NULL
    let fltWDS = Prelude.filter (\(Only a) ->
                                    case a of
                                      Nothing -> False
                                      Just a -> True)
                 bd_rows_WDS

> 
> So you do not need to write an explicit if in Haskell.  The Functor
> instance of Maybe "knows" how to deal will null cases.
> In particular, if it encounters a Nothing, it returns a Nothing,
> ignoring the provided function.
> 
> 3. Applicative allows you to apply a function to the "a"s of several "Maybe a"
> values, returning a Maybe wrapping the result of that function.
> So "f <$> x <*> y" is equivalent to
> 
> typeOfA f(TypeOfA);
> typeOfA *func(typeOfA *x, typeOfA *y) {
>   if (x && y) {
>     typeOfA *val = malloc(sizeof(typeOfA));
>     *val = f(*x, *y);
>     return val;
>   }
>   return NULL;
> }
> func(x, y);
> 
> The amount of boilerplate eliminated grows.
> 
> 3. Monad allows you to chain several null-checks without having to write
> out the nesting.  This time, f, g and h return a "Maybe a", not just an "a":
> 
> do
>   a <- f x
>   b <- g a
>   h b
>   
> or
> 
> f x >>= \a -> g a >>= \b -> h b
> 
> is equvalent to something like
> 
> typeOfA *f(typeOfA);
> typeOfA *g(typeOfA);
> typeOfA *h(typeOfA);
> typeOfA *func(typeOfA x) {
>   if (x) {
>     type *a = f(*x);
>     if (a) {
>       type *b = g(*a);
>       if (b) {
>         return f(*b);
>       }
>     }
>   }
>   return NULL;
> }
> 
> or, in Haskell:
> 
> case f x of
>   Just a -> case g a of
>     Just b -> h b
>     Nothing -> Nothing
>   Nothing -> Nothing
> 
> The Monad typeclass really pays off, as it saves you from doing the null
> checks and dereferencing.  The nested "if null then null" stuff goes all
> away, and you can chain several functions together that return a
> possible null value.
> 
>> ok reading twice the article in link, i admit there should be some
>> adavantage using Nothing instead of null, such as type, and genericity
>> and not being spcecific to alloated memory as in C ...
> 
> Exactly.  As I see it, the Functor, Applicative and Monad typeclasses
> of the Maybe type give you the tools
> necessary to reduce the boilerplate that results from forcing you to
> deal with each and every null case in your code.
> 

-- 
Damien.Mattei at unice.fr, Damien.Mattei at oca.eu, UNS / OCA / CNRS


More information about the Haskell-Cafe mailing list