Notational confusion in a basic type declaration exercise - haskell

I'm going through the new book Haskell Programming from First Principles. It seems decent, but I feel that there are some confusing holes in the explanations. I apologize if I'm missing something basic.
The last problem in chapter 5 is to fill in the ??? below so that things make sense:
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge = ???
The solution which was explained to me (after much head-scratching) goes:
g :: y -> (w, z)
g = undefined
f :: x -> y
f = undefined
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge g f v = fst (g (f v))
I'm getting hung up on this example in two ways.
First, it seems like the munge function ought to take a function as input which takes x -> y. But the way munge is defined, it seems like we supply an additional argument v to the function f first. But if f :: x -> y, then won't the expression f v be of type just y instead of x -> y?
Second, I'm struggling to understand why the x appears in the second-to-last position in the type declaration. At that point I feel like the logical next piece after the (y -> (w,x)) step should just be w, since at that stage the function g is being applied to fst and w ought to be the type of what fst returns. I can feel that I'm close, but can't quite close the gap.
Clearly I'm not understanding the notation correctly. Can anyone set me straight?
EDIT: Ok, here is a clarifying question to the second part. Is it possible to revise the munge function so that it has the following type (i.e. original type with second-to-last x application omitted)? If so what would it look like?
munge :: (x -> y) -> (y -> (w, z)) -> w

The answer is incorrect and ill-typed. f and g should be swapped:
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge g f v = fst (f (g v))
I'm not sure if that clears up your confusion.
EDIT In case it's interesting, here are more equivalent ways of writing this function and its type:
-- notice parens in type signature; `->` associates right
munge :: (x -> y) -> ((y -> (w, z)) -> (x -> w))
munge g f v = -- omitted
-- type signature omitted
munge boop _plort zOWY = fst (_plort (boop zOWY))
munge g f = fst . f . g
munge g = \f v -> fst . f . g $ v
-- don't do this please
munge = ((fst .) .) . (.)
EDIT2 It might be helpful to play around with this in GHCi, asking the inferred type of different expressions:
Prelude> let munge g f v = fst (f (g v))
Prelude> :t munge
munge :: (t1 -> t) -> (t -> (a, b)) -> t1 -> a
Prelude> :t munge head
munge head :: (t -> (a, b)) -> [t] -> a
Prelude> :t munge head (\x-> (x, not x))
munge head (\x-> (x, not x)) :: [Bool] -> Bool
Prelude> :t munge ((+1) . fst . snd . head)
munge ((+1) . fst . snd . head)
:: Num t => (t -> (a, b)) -> [(a1, (t, b1))] -> a

The solution, confusingly, is using the same variables f and g for two different things: as global names for two functions, and as parameter names in defining munge. Making a change of variable should make it clearer:
g :: y -> (w, z)
g = undefined
f :: x -> y
f = undefined
munge :: (x -> y) -> (y -> (w, z)) -> x -> w
munge f1 f2 v = fst (f2 (f1 v)) -- fst . f2 . f1 $ v
Then you would call munge on f and g will thing like
munge f g someArgumentForF
Inside munge, f (called f1) is first applied to someArgumentForF (called v) to get a value that can be passed to g (called f2). This produces a tuple, and applying fst to the tuple returns the value of type w needed as the final result.

Related

Point Free problems in Haskell

I am trying to convert the following Haskell code to point free style, to no avail.
bar f g xs = filter f (map g xs )
I'm new to Haskell and any help would be great.
Converting to pointfree style can be done entirely mechanically, though it's hard without being comfortable with the fundamentals of Haskell syntax like left-associative function application and x + y being the same as (+) x y. I will assume you are comfortable with Haskell syntax; if not, I suggest going through the first few chapters of LYAH first.
You need the following combinators, which are in the standard library. I have also given their standard names from combinator calculus.
id :: a -> a -- I
const :: a -> b -> a -- K
(.) :: (b -> c) -> (a -> b) -> (a -> c) -- B
flip :: (a -> b -> c) -> (b -> a -> c) -- C
(<*>) :: (a -> b -> c) -> (a -> b) -> (a -> c) -- S
Work with one parameter at a time. Move parameters on the left to lambdas on the right, e.g.
f x y = Z
becomes
f = \x -> \y -> Z
I like to do this one argument at a time rather than all at once, it just looks cleaner.
Then eliminate the lambda you just created according to the following rules. I will use lowercase letters for literal variables, uppercase letters to denote more complex expressions.
If you have \x -> x, replace with id
If you have \x -> A, where A is any expression in which x does not occur, replace with const A
If you have \x -> A x, where x does not occur in A, replace with A. This is known as "eta contraction".
If you have \x -> A B, then
If x occurs in both A and B, replace with (\x -> A) <*> (\x -> B).
If x occurs in just A, replace with flip (\x -> A) B
If x occurs in just B, replace with A . (\x -> B),
If x does not occur in either A or B, well, there's another rule we should have used already.
And then work inward, eliminating the lambdas that you created. Lets work with this example:
f x y z = foo z (bar x y)
-- Move parameter to lambda:
f x y = \z -> foo z (bar x y)
-- Remember that application is left-associative, so this is the same as
f x y = \z -> (foo z) (bar x y)
-- z appears on the left and not on the right, use flip
f x y = flip (\z -> foo z) (bar x y)
-- Use rule (3)
f x y = flip foo (bar x y)
-- Next parameter
f x = \y -> flip foo (bar x y)
-- Application is left-associative
f x = \y -> (flip foo) (bar x y)
-- y occurs on the right but not the left, use (.)
f x = flip foo . (\y -> bar x y)
-- Use rule 3
f x = flip foo . bar x
-- Next parameter
f = \x -> flip foo . bar x
-- We need to rewrite this operator into normal application style
f = \x -> (.) (flip foo) (bar x)
-- Application is left-associative
f = \x -> ((.) (flip foo)) (bar x)
-- x appears on the right but not the left, use (.)
f = ((.) (flip foo)) . (\x -> bar x)
-- use rule (3)
f = ((.) (flip foo)) . bar
-- Redundant parentheses
f = (.) (flip foo) . bar
There you go, now try it on yours! There is not really any cleverness involved in deciding which rule to use: use any rule that applies and you will make progress.
Both of the existing answers don't really answer your specific question in a way that's elucidating: one is "here are the rules, work it out for yourself" and the other is "here is the answer, no information about how the rules generate it."
The first three steps are really easy and consist in removing a common x from something of the form h x = f (g x) by writing h = f . g. Essentially it's saying "if you can write the thing in the form a $ b $ c $ ... $ y $ z and you want to remove the z, change all the dollars to dots, a . b . c . ... . y:
bar f g xs = filter f (map g xs)
= filter f $ (map g xs)
= filter f $ map g $ xs -- because a $ b $ c == a $ (b $ c).
bar f g = filter f . map g
= (filter f .) (map g)
= (filter f .) $ map $ g
bar f = (filter f .) . map
So this last f is the only tricky part, and it's tricky because the f is not at the "end" of the expression. But looking at it, we see that this is a function section (. map) applied to the rest of the expression:
bar f = (.) (filter f) . map
bar f = (. map) $ (.) $ filter $ f
bar = (. map) . (.) . filter
and that's how you reduce an expression when you don't have complicated things like f x x and the like appearing in it. In general there is a function flip f x y = f y x which "flips arguments"; you can always use that to move the f to the other side. Here we have flip (.) map . (.) . filter if you include the explicit flip call.
I asked lambdabot, a robot who hangs out on various Haskell IRC channels, to automatically work out the point-free equivalent. The command is #pl (pointless).
10:41 <frase> #pl bar f g xs = filter f (map g xs )
10:41 <lambdabot> bar = (. map) . (.) . filter
The point free version of bar is:
bar = (. map) . (.) . filter
This is arguably less comprehensible than the original (non-point-free) code. Use your good judgement when deciding whether to use point-free style on a case-by-case basis.
Finally, if you don't care for IRC there are web-based point-free
converters such as pointfree.io, the pointfree command line program, and other tools.

Haskell - How to write twice function using (.) f g - function composition

Here is the problem, i need to write the well known twice function
(twice= \x-> \x-> x)
but this time using (.) composition function like (.) f g.
I don't know how to solve it, cause I thought at the beginning to do like:
(.) f g = (\x -> f (g x))
with (g = f) so it would be like this
(.) f f = (\x -> f (f x))
but I have a
"Conflicting definitions for `f'"
running on GHCI
So, any suggestions ?
I don't know how you got anything other than a parse input from this:
(.) f f = (\x -> f (f x))
but the definition you gave: twice = \x -> \x -> x has nothing to do with using something "twice" - indeed if you plug in some values:
twice a b
= (\x -> \x -> x) a b
= (\x -> (\x -> x)) a b -- (rename the inner x)
= (\x -> (\y -> y)) a b
= ((\x -> (\y -> y)) a) b
= (\y -> y) b
= b
and indeed GHCi will tell you the same:
> let twice = \x -> \x -> x
> :t twice
twice :: t -> t1 -> t1
> twice "a" "b"
"b"
Now I guess you want something like this:
let twice f x = f (f x)
for example:
> let twice f x = f (f x)
> twice (+1) 5
7
as you can see twice (+1) adds 2 (or twice one).
Now how can you do this using (.)? - Well your intuition was wright:
> let twice f = f . f
> twice (+1) 5
7
concerning a module
As you asked for a module - this compiles (and loads into GHCi) fine on my system(s):
module Twice where
twice :: (a->a) -> a -> a
twice f = f . f
remark:
this only works if you include (.) from the prelude (or GHC.Base) - I suspect that you got some kind of excercise that hid the prelude - in this case you have to define (.) for yourself first (most likely another excercise)
if you need to implement it yourself:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) g f x = g (f x)

Writing Category Instance for custom Lens

I have been reading this article for understanding Lenses. I know this is different from
Edward Knett's lens package, but nonetheless it's useful for fundamentals.
So, A Lens is defined like this:
type Lens a b = (a -> b, b -> a -> a)
It has been mentioned that Lenses form a category and I have been
trying out to create an instance for Category typeclass. For a start, I
wrote the type definition for the functions:
(.) :: Lens y z -> Lens x y -> Lens x z
id :: Lens x x
And after this, I just stare it for all day. What exactly is the
thought process for writing it's definition?
I found this article (Lenses from Scratch on fpcomplete by Joseph Abrahamson) to be very good, it starts from the same representation of lenses you started with, defines composition for it and continues along the path to a representation more similar to lens
EDIT: I find type holes to be excellent when doing this kind of things:
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (_,_)
So now we have 2 holes, the first in the tuple says (output cleaned):
Found hole ‘_’ with type: x -> z
...
Relevant bindings include
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
(<.>) :: Lens y z -> Lens x y -> Lens x z
Looking hard at the bindings, we already have what we need! getB :: x -> y and getA :: y -> z together with function composition (.) :: (b -> c) -> (a -> b) -> a -> c
So we happily insert this:
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (getA . getB, _)
And continue with the second type hole, which says:
Found hole ‘_’ with type: z -> x -> x
Relevant bindings include
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
The most similar thing we have is setA :: z -> y -> y, we start by inserting a lambda, capturing the arguments:
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> _)
changing your type hole to:
Found hole ‘_’ with type: x
Relevant bindings include
x :: x
z :: z
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
we could insert x which type checks, but does not give us what we want (nothing happens when setting). The only other binding that could give us an x is setB, so we insert that:
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB _ _)
Our first type hole says:
Found hole ‘_’ with type: y
Relevant bindings include
x :: x
z :: z
setB :: y -> x -> x
getB :: x -> y
setA :: z -> y -> y
getA :: y -> z
So we need an y, looking at what is in scope, getB can give us a y if we give it a x, which we happen to have, but this would lead us to a useless lens doing nothing again. The alternative is to use setA:
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA _ _) _)
(Speeding things a little up from here on)
Again the first hole wants something of type z which he happen to have as an argument to our lambda:
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z _) _)
To fill the first type hole of type y we can use getB :: x -> y giving it the argument of our lambda:
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z (getB x)) _)
Which leaves us with one remaining type hole, which can trivially be replaced by x, leading to the final definition:
(<.>):: Lens y z -> Lens x y -> Lens x z
(getA,setA) <.> (getB,setB) = (getA . getB, \z x -> setB (setA z (getB x)) x)
You can try to define id for yourself, using type holes and hoogle if necessary
Try this:
(.) :: Lens y z -> Lens x y -> Lens x z
(getZfromY , setZinY) . (getYfromX , setYinX) = (getZfromX , setZinX)
where getZfromX someX = ...
setZinX someZ someX = ...
The idea is: combine the two getters to make the new getter, and combine the two setters to make a new setter.
For the identity, think about:
id :: Lens x x
id = (getXfromX , setXinX)
where getXfromX someX = ...
setXinX newX oldX = ...
It seems to be a fairly straighforward process. But also need to check that you get a category - this requires equational reasoning - because, for example, there is at least one more way to implement the setter of id with type x->x->x - only one of those will make a category.
So, let's start with getting functions of the right type.
Lens y z -> Lens x y -> Lens x z ==
(y->z, z->y->y) -> (x->y, y->x->x) -> (x->z, z->x->x)
It seems clear how to get x->z from x->y and y->z - compose. Well, and you have ways to construct new x from old x and new y, and a way to get old y from old x, so if you can construct new y from z and old y, you are done.
(.) (yz, zyy) (xy, yxx) = (yz . xy, \z x -> yxx (zyy z (xy x)) x)
Similarly for id:
Lens x x ==
(x->x, x->x->x)
So
id = (id, const)
So far so good, the types check. Now let's check that we've got a category. There is one law:
f . id = f = id . f
Checking one way (a bit informal, so need to bear in mind that . and id refer to different things in f . id and fg . id):
f . id = (fg, fs) . (id, const) =
(fg . id, \z x -> const (fs z (id x)) x) =
(fg, \z x -> fs z (id x)) = (fg, fs)
Checking the other way:
id . f = (id, const) . (fg, fs) =
(id . fg, \z x -> fs (const z (fg x)) x) =
(fg, \z x -> fs z x) = (fg, fs)

Applicative functors: why can fmap take a function with more than one argument?

I am getting into Haskell and found the book "learn you a Haskell" most helpful. I am up to the section on applicative functors.
I am puzzled by the following as it appears in the book:
(\x y z -> [x, y, z]) <$> (+3) <*> (*2) <*> (/2) $ 5
which yields the output:
[8.0,10.0,2.5]
First of all, I have confirmed my suspicion in ghci in regards to precedence of the operators, so that the above equals the following ugly statement:
(((\x y z -> [x,y,z]) <$> (+3)) <*> (*2) <*> (/2)) $ 5
So from that it becomes clear that the first thing that happens is the fmap call via the (<$>) infix operator.
And this is the core of what boggles my mind currently.
The definition of fmap (here shown as infix (<$>)) is:
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
But in the equation I am struggling with, (\x y z -> [x, y, z]) takes three arguments, not just one. So how could the first argument of type (a -> b) be satisfied?
I think it might have to do with partial application / currying but I cannot figure it out. I would greatly appreciate an explanation. Hope I have formulated the question well enough.
Simple answer: there are no functions with multiple arguments in Haskell!
There are two candidates for what you might call "dyadic function": a function that takes a (single!) tuple, and – by far prevalent in Haskell – curried functions. Those take just one argument, but the result is a function again.
So, to figure out what e.g. fmap (+) does, let's write
type IntF = Int -> Int
-- (+) :: Int -> IntF
-- fmap :: ( a -> b ) -> f a -> f b
-- e.g.:: (Int->IntF) -> f Int->f IntF
Test it yourself in GHCi:
Prelude> type IntF = Int -> Int
Prelude> let (#) = (+) :: Int -> IntF
Prelude> :t fmap (#)
fmap (#) :: Functor f => f Int -> f IntF
Consider a function of type
f :: a -> b -> c -> d
where d is any other type. Due to currying, this can be thought of as a function with the following type
f :: a -> (b -> c -> d)
i.e. a function that takes an a and returns function of type b -> c -> d. If you apply fmap, you have
-- the type of fmap, which is also :: (a -> r) -> (f a -> f r)
fmap :: Functor f => (a -> r) -> f a -> f r
-- the type of f
f :: a -> (b -> c -> d)
-- so, setting r = b -> c -> d
fmap f :: f a -> f (b -> c -> d)
Which is now of the right type to be used as the left-hand argument to (<*>).
Because you can take a 3-argument function, feed it just one argument, and this results in a 2-argument function. So you're going to end up with a list of 2-argument functions. You can then apply one more argument, ending up with a list of 1-argument functions, and finally apply the last argument, whereupon you end up with a list of ordinary numbers.
Incidentally, this is why Haskell has curried functions. It makes it easy to write constructs like this one which work for any number of function arguments. :-)
I personally find the applicative functor instance for functions a bit strange. I'll walk you through this example to try to understand intuitively what's going on:
>>> :t (\x y z -> [x, y, z]) <$> (+3)
... :: Num a => a -> a -> a -> [a]
>>> ((\x y z -> [x, y, z]) <$> (+3)) 1 2 3
[4,2,3]
This applies (+3) to the first parameter of the inner function. The other 2 outer parameters are passed to the inner function unmodified.
Let's add an applicative:
>>> :t (\x y z -> [x, y, z]) <$> (+3) <*> (*2)
... :: Num a => a -> a -> [a]
>>> ((\x y z -> [x, y, z]) <$> (+3) <*> (*2)) 1 2
[4,2,2]
This applies (+3) to the first argument as before. With the applicative, the first outer parameter (1) is applied (*2) and passed as the second parameter of the inner function. The second outer parameter is passed unmodified to the inner function as its third parameter.
Guess what happens when we use another applicative:
>>> :t (\x y z -> [x, y, z]) <$> (+3) <*> (*2) <*> (/2)
... :: Fractional a => a -> [a]
>>> (\x y z -> [x, y, z]) <$> (+3) <*> (*2) <*> (/2) $ 1
[4.0,2.0,0.5]
3 applications to the same parameter passed as 3 arguments to the inner function.
It's not theoretically solid explanation, but it can give an intuition about how the applicative instance of functions works.
Background
Let's start with the definition of the <*> and pure for functions as an instance of Applicative. For pure, it will take any garbage value, and return x. For <*>, you can think of it as applying x to f, getting a new function out of it, then applying it to the output of g x.
instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
Now, let's look at the definition of <$>. It is just an infix version of fmap.
(<$>) :: (Functor f) => (a -> b) -> f a -> f b
f <$> x = fmap f x
Recall that fmap has the following implementation:
instance Functor ((->) r) where
fmap f g = (\x -> f (g x))
Proving that f <$> x is just pure f <*> x
Let's start with pure f <*> x. Replace pure f with (\_ -> f).
pure f <*> x
= (\_ -> f) <*> x
Now, let's apply the definition of <*>, which is f <*> g = \q -> f q (g q).
(\_ -> f) <*> x
= \q -> (\_ -> f) q (x q)
Notice we can simplify (\_ -> f) q as just f. The function takes in whatever value we give it, and returns f.
\q -> (\_ -> f) q (x q)
= \q -> f (x q)
That looks just like our definition of fmap! And the <$> operator is just infix fmap.
\q -> f (x q)
= fmap f x
= f <$> x
Let's keep this in mind: f <$> g is just pure f <*> g.
Understanding (\x y z -> [x, y, z]) <$> (+3) <*> (*2) <*> (/2) $ 5
First step is to rewrite the left side of expression to use <*> instead of <$>. Using what we just proved in in the previous section:
(\x y z -> [x, y, z]) <$> (+3)
= pure (\x y z -> [x, y, z]) <*> (+3)
So the full expression becomes
pure (\x y z -> [x, y, z]) <*> (+3) <*> (*2) <*> (/2) $ 5
Let's simplify the first operator using the definition of <*>
pure (\x y z -> [x, y, z]) <*> (+3)
= \a -> f a (g a) --substitute f and g
= \a -> pure (\x y z -> [x, y, z]) a ((+3) a)
Now let's substitute pure x with (\_ -> x). Observe that a becomes the garbage value that's used as _, and is consumed to return the function (\x y z -> [x, y, z]).
\a -> (\_-> (\x y z -> [x, y, z])) a ((+3) a)
= \a -> (\x y z -> [x, y, z]) ((+3) a)
Now let's look back at the full expression, and tackle the next <*>. Again, let's apply the definition of <*>.
(\a -> (\x y z -> [x, y, z]) ((+3) a)) <*> (*2)
= \b -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) b ((*2) b)
Finally, let's repeat this one last time for the final <*>.
(\b -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) b ((*2) b)) <*> (/2)
= \c -> (\b -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) b ((*2) b)) c ((/2) c)
Notice that it's a function that takes a single value. We'll feed it 5.
(\c -> (\b -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) b ((*2) b)) c ((/2) c)) 5
(\5 -> (\b -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) b ((*2) b)) 5 ((/2) 5))
(\b -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) b ((*2) b)) 5 (2.5 )
(\5 -> (\a -> (\x y z -> [x, y, z]) ((+3) a)) 5 ((*2) 5)) (2.5 )
(\a -> (\x y z -> [x, y, z]) ((+3) a)) 5 (10 ) (2.5 )
(\5 -> (\x y z -> [x, y, z]) ((+3) 5)) (10 ) (2.5 )
(\x y z -> [x, y, z]) (8 ) (10 ) (2.5 )
(\x y z -> [x, y, z]) (8) (10) (2.5)
= [8, 10, 2.5]
And that's how we get the final answer.

How can I understand "(.) . (.)"?

I believe I understand fmap . fmap for Functors, but on functions it's hurting my head for months now.
I've seen that you can just apply the definition of (.) to (.) . (.), but I've forgot how to do that.
When I try it myself it always turns out wrong:
(.) f g = \x -> f (g x)
(.) (.) (.) = \x -> (.) ((.) x)
\x f -> (.) ((.) x) f
\x f y -> (((.)(f y)) x)
\x f y g-> (((.)(f y) g) x)
\x f y g-> ((f (g y)) x)
\x f y g-> ((f (g y)) x):: t2 -> (t1 -> t2 -> t) -> t3 -> (t3 -> t1) -> t
If "just applying the definition" is the only way of doing it, how did anybody come up with (.) . (.)?
There must be some deeper understanding or intuition I'm missing.
Coming up with (.) . (.) is actually pretty straightforward, it's the intuition behind what it does that is quite tricky to understand.
(.) gets you very far when rewriting expression into the "pipe" style computations (think of | in shell). However, it becomes awkward to use once you try to compose a function that takes multiple arguments with a function that only takes one. As an example, let's have a definition of concatMap:
concatMap :: (a -> [b]) -> [a] -> [b]
concatMap f xs = concat (map f xs)
Getting rid of xs is just a standard operation:
concatMap f = concat . map f
However, there's no "nice" way of getting rid of f. This is caused by the fact, that map takes two arguments and we'd like to apply concat on its final result.
You can of course apply some pointfree tricks and get away with just (.):
concatMap f = (.) concat (map f)
concatMap f = (.) concat . map $ f
concatMap = (.) concat . map
concatMap = (concat .) . map
But alas, readability of this code is mostly gone. Instead, we introduce a new combinator, that does exactly what we need: apply the second function to the final result of first one.
-- .: is fairly standard name for this combinator
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)
concatMap = concat .: map
Fine, that's it for motivation. Let's get to the pointfree business.
(.:) = \f g x y -> f (g x y)
= \f g x y -> f ((g x) y)
= \f g x y -> f . g x $ y
= \f g x -> f . g x
Now, here comes the interesting part. This is yet another of the pointfree tricks that usually helps when you get stuck: we rewrite . into its prefix form and try to continue from there.
= \f g x -> (.) f (g x)
= \f g x -> (.) f . g $ x
= \f g -> (.) f . g
= \f g -> (.) ((.) f) g
= \f -> (.) ((.) f)
= \f -> (.) . (.) $ f
= (.) . (.)
As for intuition, there's this very nice article that you should read. I'll paraphrase the part about (.):
Let's think again about what our combinator should do: it should apply f to the result of result of g (I've been using final result in the part before on purpose, it's really what you get when you fully apply - modulo unifying type variables with another function type - the g function, result here is just application g x for some x).
What it means for us to apply f to the result of g? Well, once we apply g to some value, we'll take the result and apply f to it. Sounds familiar: that's what (.) does.
result :: (b -> c) -> ((a -> b) -> (a -> c))
result = (.)
Now, it turns out that composition (our of word) of those combinators is just a function composition, that is:
(.:) = result . result -- the result of result
You can also use your understanding of fmap . fmap.
If you have two Functors foo and bar, then
fmap . fmap :: (a -> b) -> foo (bar a) -> foo (bar b)
fmap . fmap takes a function and produces an induced function for the composition of the two Functors.
Now, for any type t, (->) t is a Functor, and the fmap for that Functor is (.).
So (.) . (.) is fmap . fmap for the case where the two Functors are (->) s and (->) t, and thus
(.) . (.) :: (a -> b) -> ((->) s) ((->) t a) -> ((->) s) ((->) t b)
= (a -> b) -> (s -> (t -> a)) -> (s -> (t -> b))
= (a -> b) -> (s -> t -> a ) -> (s -> t -> b )
it "composes" a function f :: a -> b with a function of two arguments, g :: s -> t -> a,
((.) . (.)) f g = \x y -> f (g x y)
That view also makes it clear that, and how, the pattern extends to functions taking more arguments,
(.) :: (a -> b) -> (s -> a) -> (s -> b)
(.) . (.) :: (a -> b) -> (s -> t -> a) -> (s -> t -> b)
(.) . (.) . (.) :: (a -> b) -> (s -> t -> u -> a) -> (s -> t -> u -> b)
etc.
Your solution diverges when you introduce y. It should be
\x f y -> ((.) ((.) x) f) y :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) ((.) x) f) y z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> ((.) x (f y)) z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
-- Or alternately:
\x f y z -> (x . f y) z :: (c -> d) -> (a -> b -> c) -> a -> b -> d
\x f y z -> (x (f y z)) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
Which matches the original type signature: (.) . (.) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(It's easiest to do the expansion in ghci, where you can check each step with :t expression)
Edit:
The deeper intution is this:
(.) is simply defined as
\f g -> \x -> f (g x)
Which we can simplify to
\f g x -> f (g x)
So when you supply it two arguments, it's curried and still needs another argument to resolve.
Each time you use (.) with 2 arguments, you create a "need" for one more argument.
(.) . (.) is of course just (.) (.) (.), so let's expand it:
(\f0 g0 x0 -> f0 (g0 x0)) (\f1 g1 x1 -> f1 (g1 x1)) (\f2 g2 x2 -> f2 (g2 x2))
We can beta-reduce on f0 and g0 (but we don't have an x0!):
\x0 -> (\f1 g1 x1 -> f1 (g1 x1)) ((\f2 g2 x2 -> f2 (g2 x2)) x0)
Substitute the 2nd expression for f1...
\x0 -> \g1 x1 -> ((\f2 g2 x2 -> f2 (g2 x2)) x0) (g1 x1)
Now it "flips back"! (beta-reduction on f2):
This is the interesting step - x0 is substituted for f2 -- This means that x, which could have been data, is instead a function.
This is what (.) . (.) provides -- the "need" for the extra argument.
\x0 -> \g1 x1 -> (\g2 x2 -> x0 (g2 x2)) (g1 x1)
This is starting to look normal...
Let's beta-reduce a last time (on g2):
\x0 -> \g1 x1 -> (\x2 -> x0 ((g1 x1) x2))
So we're left with simply
\x0 g1 x1 x2 -> x0 ((g1 x1) x2)
, where the arguments are nicely still in order.
So, this is what I get when I do a slightly more incremental expansion
(.) f g = \x -> f (g x)
(.) . g = \x -> (.) (g x)
= \x -> \y -> (.) (g x) y
= \x -> \y -> \z -> (g x) (y z)
= \x y z -> (g x) (y z)
(.) . (.) = \x y z -> ((.) x) (y z)
= \x y z -> \k -> x (y z k)
= \x y z k -> x (y z k)
Which, according to ghci has the correct type
Prelude> :t (.) . (.)
(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c
Prelude> :t \x y z k -> x (y z k)
\x y z k -> x (y z k)
:: (t1 -> t) -> (t2 -> t3 -> t1) -> t2 -> t3 -> t
Prelude>
While I don't know the origins of this combinator, it is likely that it was
developed for use in combinatory logic, where you work strictly with combinators,
so you can't define things using more convenient lambda expressions. There may be
some intuition that goes with figuring these things out, but I haven't found it.
Most likely, you would develop some level of intuition if you had to do it enough.
It's easiest to write equations, combinators-style, instead of lambda-expressions: a b c = (\x -> ... body ...) is equivalent to a b c x = ... body ..., and vice versa, provided that x does not appear among {a,b,c}. So,
-- _B = (.)
_B f g x = f (g x)
_B _B _B f g x y = _B (_B f) g x y
= (_B f) (g x) y
= _B f (g x) y
= f ((g x) y)
= f (g x y)
You discover this if, given f (g x y), you want to convert it into a combinatory form (get rid of all the parentheses and variable repetitions). Then you apply patterns corresponding to the combinators' definitions, hopefully tracing this derivation backwards. This is much less mechanical/automatic though.

Resources