Search code examples
haskelloption-type

Haskell Maybe adder without Arguments


I am wondering if it is possible to wirte the Function

add :: Maybe Int -> Maybe Int
add Just x = Just (x+1)
add Nothing = Nothing

without the x. Similar to

f = Just.(+1)

However

add Just = Just.(+1)

throws an error: Equations for 'add' have different numbers of arguments. Can someone please explain me why this does not work?


Solution

  • You need to pattern match somewhere - there's no way to "get the value out" without doing so. You can use some unsafe function, like fromJust, but

    • it's unsafe - doing a case and pattern matching is better
    • it's still doing pattern matching in it, so you're not really avoiding doing it.

    The "proper modular" way to do this, would be to write this common pattern as a higher-order function, so that you can reuse it:

    • in the Nothing case, you return Nothing
    • in the Just case, you return Just of some function applied to the arg inside

    Taking into account the two things above, we reach the following

    maybeMap :: (a -> b) -> Maybe a -> Maybe b
    maybeMap _ Nothing = Nothing
    maybeMap f (Just x) = Just (f x)
    

    which you can now use to write your desired function:

    add :: Maybe Int -> Maybe Int
    add x = maybeMap (+1) x
    -- or we can eta reduce it - taking an argument and calling a function with it is the same as just returning the function directly
    add = maybeMap (+1)
    

    This function is traditionally called map, because you're mapping "the values inside a container" to something else.

    This is something that you very often need to do for different "containers" (and some other kinds of things), so we have a type class for it in the standard library (base), named after some theoretical things:

    class Functor f where
      fmap :: (a -> b) -> f a -> f b
    instance Functor [] where
      fmap = map
    instance Functor Maybe where
      fmap = maybeMap
    

    Additionally, the error you're seeing is something else entirely. In Haskell, when writing a function definition, your different cases are not allowed to have different numbers of arguments taken:

    -- not allowed, since in the first case you've taken two args,
    -- but in the second you've only taken one.
    bla :: Integer -> Integer -> Integer
    bla 0 y = 42
    bla x = id
    
    -- this is fine, in both cases we have two arguments
    bla :: Integer -> Integer -> Integer
    bla 0 y = 42
    bla x y = y