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).
funct
isn't relevant, can you at least edit the question so that you tell us its type? – Robin Zigmond