4

I have to give a (simple) talk about Yesod. And yes,.. i've never or really really rarely used haskell as well. University lecturer.....huh.

So i read a book about yesod and in some chapters the author is using some operators like <$> and <*>. Can someone explain in easy words, what this operators do? Its pretty hard to google for that chars and if tried to read the documentation of Control.Applicative but to be honest, its hard to get for an haskell beginner.

so i hope anyone have a simple answer for me :)

an example of the book where these operators are used:

......
personForm :: Html -> MForm Handler (FormResult Person, Widget)
personForm = renderDivs $ Person
    <$> areq textField "Name" Nothing
    <*> areq (jqueryDayField def
        { jdsChangeYear = True -- give a year dropdown
        , jdsYearRange = "1900:-5" -- 1900 till five years ago
        }) "Birthday" Nothing
    <*> aopt textField "Favorite color" Nothing
    <*> areq emailField "Email address" Nothing
    <*> aopt urlField "Website" Nothing
data Person = Person
    { personName          :: Text
    , personBirthday      :: Day
    , personFavoriteColor :: Maybe Text
    , personEmail         :: Text
    , personWebsite       :: Maybe Text
    }
  deriving Show
.....

.....................................

Hey,

Thanks a lot and amazingly most of the answers are useful. Sadly a only can hit "solved" on one answer. Thanks a lot, the tutorial (that I really didn't find on Google) is pretty good

recursion.ninja
  • 5,377
  • 7
  • 46
  • 78
Smoki
  • 551
  • 2
  • 9
  • 28
  • Well are those really different from `Control.Applicative`? Because (as a Haskell beginner), after I understoond Functors, Applicatives were smooth. – Bartek Banachewicz Nov 07 '13 at 11:45
  • Possible duplicate of http://stackoverflow.com/questions/6280585/need-to-know-what-and-do-in-haskell – mhwombat Nov 07 '13 at 11:53

4 Answers4

9

I am always very careful when making answers that are made up mostly of links, but this is one amazing tutorial that explains Functors, Applicatives and gives a bit of understaning on Monads.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
6

The simplest answer is the type, of course. These operators come from type typeclasses Functor and its subclass Applicative.

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

(<$>) = fmap -- synonym

class Functor f => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

The simplest intuitive answer is that Functors and Applicatives let you annotate simple values with "metadata" and (<$>), (<*>), and friends let you transform your "regular" value-level functions to work on "annotated" values.

go x y                    -- works if x and y are regular values
go <$> pure x <*> pure y  -- uses `pure` to add "default" metadata
                          -- but is otherwise identical to the last one

Like any simple answer, it's kind of a lie, though. "Metadata" is a very oversimplified term. Better ones are "computational context" or "effect context" or "container".

If you're familiar with Monads then you are already very familiar with this concept. All Monads are Applicatives and so you can think of (<$>) and (<*>) as providing an alternative syntax for some do notation

do x_val <- x                     go <$> x
   y_val <- y                        <*> y
   return (go x_val y_val)

It has fewer symbols and emphasizes the idea of "applying" go to two arguments instead of emphasizing the imperative notion of "get the value that x generates, then get the value that y generates, then apply those values to go, then re-wrap the result" like do syntax does.


One final intuition I can throw out there is to think of Applicative in a very different way. Applicative is equivalent to another class called Monoidal.

class Functor f => Monoidal f where
  init :: f ()                             -- similar to pure
  prod :: f a -> f b -> f (a, b)           -- similar to (<*>)

so that Monoidal Functors let you (a) instantiate them with a trivial value

  init :: [()]
  init = []

  init :: Maybe ()
  init = Just ()

and also smash two of them together to produce their product

  prod :: [a] -> [b] -> [(a, b)]
  prod as bs = [(a, b) | a <- as, b <- bs]

  prod :: Maybe a -> Maybe b -> Maybe (a, b)
  prod (Just a) (Just b) = (Just (a, b))
  prod _        _        = Nothing

This means that with a Monoidal functor you can smash a whole lot of values together and then fmap a value-level function over the whole bunch

go <$> maybeInt `prod` (maybeChar `prod` maybeBool) where
  go :: (Int, (Char, Bool)) -> Double                       -- it's pure!
  go (i, (c, b)) = ...

which is essentially what you're doing with (<$>) and (<*>), just with fewer tuples

go <$> maybeInt <*> maybeChar <*> maybeBool where
  go :: Int -> Char -> Bool -> Double
  go i c b = ...

Finally, here's how you convert between the two notions

-- forward
init     = pure ()
prod x y = (,) <$> x <*> y

-- back
pure a   = const a <$> init
f <*> x  = ($) <$> prod f x

which shows how you can think of (<*>) as taking a normal value-level application ($) and injecting it up into the product inside of the Functor.

J. Abrahamson
  • 72,246
  • 9
  • 135
  • 180
5

I don't suppose it helps to say that <$> is just an infix synonym for fmap. However, maybe these examples help clarify:

GHCi> (*2) <$> (Just 3)
Just 6
GHCi> (*2) <$> (Nothing)
Nothing
GHCi> (*3) <$> (Right 7)
Right 21
GHCi> (*2) <$> (Left "error")
Left "error"
GHCi>  (+ 1) <$> [2,4,6,8]
[3,5,7,9]

Now compare that to this:

GHCi> (*) <$> (Just 2) <*> (Just 5)
Just 10
GHCi> (*) <$> (Just 2) <*> (Nothing)
Nothing
GHCi> (*) <$> (Right 3) <*> (Right 7)
Right 21
GHCi> (*) <$> (Left "error") <*> (Right 7)
Left "error"
GHCi> (+) <$> [1,2,3] <*> [10,20,30]
[11,21,31,12,22,32,13,23,33]
GHCi> (+) <$> [1,2,3] <*> []
[]

And then to this:

GHCi> (Just (*2)) <*> (Just 5)
Just 10
GHCi> (Right (*3)) <*> (Right 7)
Right 21
GHCi> [(+1),(+2),(+3)] <*> [10,20,30]
[11,21,31,12,22,32,13,23,33]

Really, that should show you all you need to know for lecture purposes, assuming you have learned from this that (*) <$> (Just 2) <*> (Just 5) is equivalent to Just (2 * 5)

(In the first set of examples, by the way, the functions on the left hand side are all applicatives.)

Put simply, <$> takes the function on the left and lifts it into the context of the "things in boxes" on the right, so that it can be applied to the things in boxes, in a way that obeys the special rules of the boxes (e.g. Nothng causing the whole chain to fail).

<*> takes a partially-bound function in a box on the left and applies it to the value in a box on the right. A partially bound function being one which has been given some but not all of its arguments. So (*) <$> (Right 3) <*> (Right 7) <*> (Right 4) would fail - with a not-very-helpful error message - because once * has been applied to 3 and 7 it is no longer a partial function and nobody knows what to do with the 4.

Used together, <$> and <*> allow a function to be applied to its arguments, all inside a box. You get the result in a box.

This can all only be done if the box is itself a functor; that is the crucial constraint for all of this. A functor is a function for which somebody has defined an fmap function which allows it to be transformed from a function that applies to one type into a function that applies to another type (while not changing the essential character of the function). If you like, Monads (boxes for things) know how to transform functions so that they can be applied to their things.

itsbruce
  • 4,825
  • 26
  • 35
3

If you're not ready to learn about functors, applicatives, and monads yet, this may give you an intuition for how to use <$> and <*>. (I myself learned how to use them by looking at examples, before I really understood that other stuff.) Without the <$> and <*>, the first part of that code would look something like this:

......
personForm :: Html -> MForm Handler (FormResult Person, Widget)
personForm = do
    name <- areq textField "Name" Nothing
    bday <- areq (jqueryDayField def
        { jdsChangeYear = True -- give a year dropdown
        , jdsYearRange = "1900:-5" -- 1900 till five years ago
        }) "Birthday" Nothing
    colour <- aopt textField "Favorite color" Nothing
    email <- areq emailField "Email address" Nothing
    url <- aopt urlField "Website" Nothing
    renderDivs $ Person name bday colour email url

In other words, <$> and <*> can eliminate the need to create a lot of symbols that we only use once.

mhwombat
  • 8,026
  • 28
  • 53