Understanding Functions as Applicatives in Haskell

Related searches

I've recently been trying to learn Haskell with the "Learn You a Haskell" and have been really struggling with understanding functions as Applicatives. I should point out that using other types of Applicatives like Lists and Maybe I seem to understand well enough to use them effectively.

As I tend to do when trying to understand something is I tried to play with as many examples as I could and once the pattern emerges things tend to make sense. As such I tried a few examples. Attached are my notes of several examples I tried along with a diagram I drew to try to visualize what was happening.

The definition of funct doesnt seem to relevant to the outcome but in my tests I used a function with the following definition:

funct :: (Num a) => a -> a -> a -> a

At the bottom I tried to show the same thing as in the diagrams just using normal math notation.

So all of this is well and good, I can understand the pattern when I have some function of an arbitrary number of arguments (though needs 2 or more) and apply it to a function that takes one argument. However intuitively this pattern doesn't make that much sense to me.

So here are the specific questions I have:

What is the intuitive way to understand the pattern I'm seeing, particularly if i view an Applicative as a container (which is how I view Maybe and lists)?

What is the pattern when the function on the right of the <*> takes more than a single argument (I've mostly been using the function (+3) or (+5) on the right)?

why is the function on the right hand side of the <*> applied to the second argument of the function on the left side. For example if the function on the right hand side were f() then funct(a,b,c) turns into funct (x, f(x), c)?

Why does it work for funct <*> (+3) but not for funct <*> (+)? Moreover it DOES work for (\ a b -> 3) <*> (+)

Any explanation that gives me a better intuitive understanding for this concept would be greatly appreciated. I read other explanations such as in the book I mentioned that explains functions in terms of ((->)r) or similar patterns but even though I know how to use the ->) operator when defining a function I'm not sure i understand it in this context.

Extra Details:

I want to also include the actual code I used to help me form the diagrams above.

First I defined funct as I showed above with:

funct :: (Num a) => a -> a -> a -> a

Throughout the process i refined funct in various ways to understand what was going on.

Next I tried this code:

funct a b c = 6 
functMod =  funct <*> (+3)
functMod 2 3

Unsuprisingly the result was 6

So now I tried just returning each argument directly like this:

funct a b c = a
functMod =  funct <*> (+3)
functMod 2 3 -- returns 2

funct a b c = b
functMod =  funct <*> (+3)
functMod 2 3 -- returns 5

funct a b c = c
functMod =  funct <*> (+3)
functMod 2 3 -- returns 3

From this I was able to confirm the second diagram is what was taking place. I repeated this patterns to observe the third diagram as well (which is the same patterns extended on top a second time).

You can usually understand what a function is doing in Haskell if you substitute its definition into some examples. You already have some examples and the definition you need is <*> for (->) a which is this:

(f <*> g) x = f x (g x)

I don't know if you'll find any better intuition than just using the definition a few times.

On your first example we get this:

  (funct <*> (+3)) x
= funct x ((+3) x)
= funct x (x+3)

(Since there was nothing I could do with funct <*> (+3) without a further parameter I just applied it to x - do this any time you need to.)

And the rest:

  (funct <*> (+3) <*> (+5)) x
= (funct x (x+3) <*> (+5)) x
= funct x (x+3) x ((+5) x)
= funct x (x+3) x (x+5)

  (funct <*> (+)) x
= funct x ((+) x)
= funct x (x+)

Notice you can't use the same funct with both of these - in the first it can take four numbers, but in the second it needs to take a number and a function.

  ((\a b -> 3) <*> (+)) x
= (\a b -> 3) x (x+)
= (\b -> 3) (x+)
= 3

  (((\a b -> a + b) <*> (+)) x
= (\a b -> a + b) x (x+)
= x + (x+)
= type error

Haskell functions as functors, applicatives and monads, Explaining how the Functor instance for functions shown above satisfies these laws is a great exercise in mind-bending Haskell notation, and� An applicative (also known as an applicative functor) allows a function that takes any number of arguments to be mapped over the values in a type. Applicatives in Haskell are defined by an Applicative type class, which extends the Functor type class. The Applicative type class defines at least two functions - pure and (<*>) 2.

You can view the function monad as a container. Note that it's really a separate monad for every argument-type, so we can pick a simple example: Bool.

type M a = Bool -> a

This is equivalent to

data M' a = M' { resultForFalse :: a
               , resultForTrue :: a  }

and the instances could be defined

instance Functor M where            instance Functor M' where
  fmap f (M g) = M g'                 fmap f (M' gFalse gTrue) = M g'False g'True
   where g' False = f $ g False        where g'False = f $ gFalse
         g' True  = f $ g True               g'True  = f $ gTrue

and similar for Applicative and Monad.

Of course this exhaustive case-listing definition would become totally impractical for argument-types with more than a few possible values, but it's always the same principle.

But the important thing to take away is that the instances are always specific for one particular argument. So, Bool -> Int and Bool -> String belong to the same monad, but Int -> Int and Char -> Int do not. Int -> Double -> Int does belong to the same monad as Int -> Int, but only if you consider Double -> Int as an opaque result type which has nothing to do with the Int-> monad.

So, if you're considering something like a -> a -> a -> a then this is not really a question about applicatives/monads but about Haskell in general. And therefore, you shouldn't expect that the monad=container picture gets you anywhere. To understand a -> a -> a -> a as a member of a monad, you need to pick out which of the arrows you're talking about; in this case it's only the leftmost one, i.e. you have the value M (a->a->a) in the type M=(a->) monad. The arrows between a->a->a do not participate in the monadic action in any way; if they do in your code, then it means you're actually mixing multiple monads together. Before you do that, you should understand how a single monad works, so stick to examples with only a single function arrow.

Functors, Applicative Functors and Monoids, Be sure that you understand how function composition works. Many times, you can intuitively see how these laws hold because the types act like containers or� Applicatives to the Rescue. This is exactly what the Applicative typeclass is for. It has two main functions: pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b. The pure function takes some value and wraps it in a minimal context. The <*> function, called sequential application, takes two parameters. First, it takes a function wrapped in the context.

Applicatives: One Step Further — Monday Morning Haskell, Applicatives to the Rescue The <*> function, called sequential application, takes two parameters. First, it takes a function wrapped in the context. Next, a wrapped value. Its output is the result of applying the function to the value, rewrapped in the context. In this Haskell workshop you will learn what functors, monads, and applicatives are and take your understanding from intuition to cemented knowledge. Upcoming Events Discovering Functors, Monads, and Applicatives

Applicative functor, When covering the vital Functor and Monad type classes, we glossed over a third type class: Applicative , the class for applicative functors. Like monads� Essentially, applicatives are about "mapping over" functions of multiple parameters. The product type 'a * 'b, as its name implies, is similar to multiplication. The identity element of multiplication is 1, meaning that 1 * a = a * 1 = 1. Similarily, the product type has an identity, the unittype.

Haskell/Applicative functors, Browse other questions tagged haskell applicative or ask your own question. Understanding Functions as Applicatives in Haskell. 3. Applicative functor laws violation.

Haskell library #2: Hascore. The Hascore library gives you the means to describe music. You use this library to create, analyze, and manipulate music in various ways. An interesting aspect of this particular library is that it helps you see music in a new way.

Comments
  • I'm struggling to understand what you're really asking about. Even if the definition of funct isn't relevant, can you at least edit the question so that you tell us its type?
  • My question was basically just looking for understanding, but i added specific questions below.. I will add a definition for funct though. and if you can think of anyway I can add additional clarity please let me know.
  • @RobinZigmond I've updated the question per your suggestion. Is this better or is there any other way I could help clarify what I'm asking?
  • I'm afraid that doesn't really clear anything up. We need to see some actual code you've tried to run, together with a description of what you expected, and what the actual result was instead.
  • @RobinZigmond Alright, give me a minute ill share all the bits of the code I used to draw the diagram above in the first place.
  • I'm a bit confused by your example, I couldnt get past the first part. because it wouldnt run given how function i defined, it takes 3 arguments not 2. So funct x (x+3) wouldnt work, it would just produce another function that takes one argument. Is that a mistake on your part or was that intentional?
  • Not a mistake, that's just what it is. If funct has type Num a => a -> a -> a -> a then funct <*> (+3) has type Num a => a -> a -> a. It's not a problem, you just have a function. If you want to see a concrete result from it try calculating out something like (funct <*> (+3)) 4 5 (or type that into ghci).
  • One issue, which is my fault not yours, is the part of the book I'm stuck on is before the part where they explain what a Monad is in haskell. For that reason alone I am struggling to understand your answer. Perhaps that is where the book got things wrong, not explaining Monads first?
  • @JeffreyPhillipsFreeman no, a monad is just a slightly stronger applicative. I used the word because it's shorter to write; you can replace every "monad" with "applicative" in my answer. But actually I would recommend that you leave aside the function-applicative completely until you are more firm with Haskell in general, including with monads.
  • In fact, it is my personal opinion that it was a mistake to include the Functor ((->)a) etc. instances in the libraries at all – sure they can be useful, but it's really confusing because we're all the time dealing with function arrows in a non-monadic way, so it becomes hard to see where the monad actually is that you're dealing with. There's a more explicit name for the function monad which avoids this problem: Reader.
  • Directly using the Functor instance may be confusing, but its definition is necessary for the Applicative instance; IMO f <$> g <*> h == \x -> f (g x) (h x) is a useful idiom to learn, even without understanding exactly why it works.
  • @chepner well, I would favour writing that as f <<< g&&&h (though that would require f to be uncurried). What I don't get is why people have an aversion against anything from Control.Arrow (though functions are an instance of category/arrow in an extremely obvious way!), but don't mind the Functor/Applicative/Monad (a->) instances, which cause lots of confusion not only for beginners.