5

When I have the two functions:

a)

three :: Int -> Maybe Int
three a
 | a == 3 = Just 3
 | otherwise = Nothing

b)

takeOne :: Int -> Int
takeOne a = (a - 1)

how do I call function a as a parameter to function b? i.e How do I let function b accept a 'Maybe Int' in place of an 'Int'?

At the minute when I try

takeOne (three 3)

I get the error:

ERROR - Type error in application
*** Expression     : takeThree (three 3)
*** Term           : three 3
*** Type           : Maybe Int
*** Does not match : Int

Thanks.

2 Answers2

15

You've got a few options, but I'd say the easiest is fmap:

fmap :: Functor f => (a -> b) -> f a -> f b

Example:

> fmap takeOne $ three 3
Just 2
> fmap takeOne $ three 2
Nothing

Another option would be to use the function maybe, which takes a default value, a function to apply to any value inside the Just, and then the Maybe a to apply this to. An example should make it clear

> maybe 0 takeOne $ three 3
2
> maybe 0 takeOne $ three 2
0

Another alternative if you just want to give a default value is to use the function fromMaybe from Data.Maybe:

> import Data.Maybe
> fromMaybe 0 $ three 3
3
> fromMaybe 0 $ three 2
0

In Haskell, there is a typeclass called Functor defined as

class Functor f where
    fmap :: (a -> b) -> f a -> f b

There are many, many types that are instances of Functor. In fact, all parametrized data structures are Functors, as are all Applicatives and Monads. The easiest mental model of a Functor is that it's just a fancy name for a container. For lists, fmap = map, for example. All it does is map a function over the elements inside a container.

Some more examples are:

> fmap (+1) (Left "error")
Left "error"
> fmap (+1) (Right 1)
Right 2

> x <- fmap (++", world") getLine
Hello
> x
Hello, world

> fmap (+1) [1..5]
[2,3,4,5,6]

> fmap (+1) ("fst", 2)
("fst", 3)

Even functions are Functors! Here fmap = (.), it's just normal function composition:

> let lengthPlusOne = fmap (+1) length
> lengthPlusOne "Hello"
6
bheklilr
  • 53,530
  • 6
  • 107
  • 163
  • 3
    And, if this is confusing, you might want to check out: http://learnyouahaskell.com/functors-applicative-functors-and-monoids – sixbitproxywax Oct 15 '14 at 16:19
  • 4
    Note that you'll frequently see `fmap f $ g` written as `f <$> g` using the function [`(<$>)`](http://hackage.haskell.org/package/base-4.7.0.1/docs/Data-Functor.html#v:-60--36--62-) from [Data.Functor](http://hackage.haskell.org/package/base-4.7.0.1/docs/Data-Functor.html). – Christian Conkle Oct 15 '14 at 18:01
  • Thanks for your help so far, it's really appreciated. I'm struggling to implement `fmap` into my code. The functions I'm using have multiple parameters and as I'm not sure what role the `$` symbol plays I don't know where to put it. – Charles Del Lar Oct 15 '14 at 21:19
  • The `$` function has the following definition: `f $ x = f x`. What makes it useful is that it has very low precedence in terms of order of operations. The hand-wavy quick explanation is that `$` computes what's on the right and passes it to what's on the left, so you can write code like `f $ g $ h x` instead of `f (g (h x))`, or even more usefully something like `map (+1) $ filter even $ zip [1..10] $ f x` instead of `map (+1) (filter even (zip [1..10] (f x)))`. Luckily for you, you don't need to implement `fmap` at all, it's already defined for you for many data types. Just use it as is. – bheklilr Oct 15 '14 at 21:25
  • @CharlesDelLar One thing to look out for with `$` is that it works best when what's on the right only takes 1 parameter. So something like `map $ (+1) $ [1..10]` won't work, while `map (+1) $ [1..10]` will. There are other explanations of this operator though, such as [this one](http://stackoverflow.com/questions/940382/haskell-difference-between-dot-and-dollar-sign), which might help make things more clear. – bheklilr Oct 15 '14 at 21:26
  • Does it matter that the parameters of the functions I'm using are data types that I have defined myself? – Charles Del Lar Oct 15 '14 at 21:36
  • @CharlesDelLar Not in the least. The type of `$` is just `(a -> b) -> a -> b`. The actual types used can be anything and everything under the sun. You should be mindful that actually much of what you see in Haskell is defined in Haskell other than a small core of low level stuff like `IO` and primitive types. The `Maybe` data type has its declaration in plain old Haskell, same with `Either`, `Functor`, and many more. Just about every function you use that doesn't do IO is written in Haskell as well, like `map` and `filter`. – bheklilr Oct 15 '14 at 21:40
  • @CharlesDelLar For example: `data MyData = MyData Int String`; `f :: MyData -> String`; `f (MyData i s) = show i ++ " " ++ s`; `f $ MyData 1 "test"` would return `"1 test"`. – bheklilr Oct 15 '14 at 21:40
0

One other option of course is to write your own.

data IAcceptedAMaybeInt = GotAnswer Int | NothingAtTheMoment deriving Show

pleaseAcceptAMaybeInt f g a = case g a of
                                Just b    -> GotAnswer (f b)
                                otherwise -> NothingAtTheMoment

Output:

*Main> pleaseAcceptAMaybeInt takeOne three 3
GotAnswer 2

*Main> pleaseAcceptAMaybeInt takeOne three 2
NothingAtTheMoment
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61