I am trying to find the type of (.).(.)
in Haskell, manually.
My attempt was the following:
(.).(.) = \x -> (.).(.) x
(.) :: ( b -> c ) -> (( a -> b ) -> (a -> c))
(.) :: (d -> e) -> ((f -> d) -> (g -> e))
(.) :: (h -> i) -> (j -> h) -> (h -> k)
-----------------------------------------------------------------------------------------------------
b ~ (d -> e) ~ (j -> h) -> (h -> k)
c ~ ((f -> d) -> (g -> e))
a ~ (h -> i)
d ~ h
e ~ i
so (a->c)~ (h->i) -> ((f -> h) -> (g -> i))
What is wrong in my way of thinking? The actual type is
(.).(.) :: (b -> c) -> (a1 -> a2 -> b) -> a1 -> a2 -> c
Let's look at the equivalent (.) (.) (.)
The first dot type:
(b -> c) -> (a -> b) -> a -> c
Second:
(e -> f) -> (d -> e) -> d -> f
Hence:
b
is the same as e -> f
c
is the same as (d -> e) -> d -> f
Third:
(h -> i) -> (g -> h) -> g -> i
Hence:
a
is the same as h -> i
b
is the same as (g -> h) -> g -> i
e -> f
is (g -> h) -> g -> i
e
is g -> h
f
is g -> i
Since (.) (.) (.) :: a -> c
from the first dot type, we have:
a -> c
is (h -> i) -> (d -> e) -> d -> f
substituting e
and f
:
(h -> i) -> (d -> g -> h) -> d -> g -> I
Answering your question, I think that what is wrong with your thinking are last two lines e.g.:
d ~ h
,
e ~ i
Your derivation should have been :
(.).(.) = (.) (.) (.)
(.) :: ( b -> c ) -> ( a -> b ) -> (a -> c)
(.) :: (d -> e) -> (f -> d) -> (g -> e) -- WRONG
(.) :: (h -> i) -> (j -> h) -> (h -> k) -- WRONG
(.) :: (d -> e) -> (f -> d) -> (f -> e) -- correct
(.) :: (h -> i) -> (j -> h) -> (j -> i) -- correct
Thus the overall type is
(.) (.) (.) :: a -> c , b ~ b
~ (h->i) -> (f -> d ) -> (f -> e ) , (j -> h) -> (j -> i) ~
( d -> e )
--------------------------------------------
(h->i) -> (f -> j -> h) -> (f -> j -> i)
Or,
( h -> i)
-> (f -> j -> h)
--------------------------------------------
-> (f -> j -> i)
The types of such composition chains are often easier to follow with (>>>) = flip (.)
:
(.) . (.) = comp2 . comp1
= comp1 >>> comp2 where comp1 = (.) ; comp2 = (.)
(>>>) :: ( a -> b ) ->
( b -> c ) -> (a -> c)
comp1 :: (e->f) -> ((d->e) -> (d->f))
comp2 :: ( h -> i ) -> ((g->h) -> (g->i))
-------------------------------------------------------------------------
(e->f) -> ((g->h) -> (g->i))
h~(d->e) i~(d->f)
-------------------------------------------------------------------------
(e->f) -> (g->d->e) -> (g->d->f)
Thus, again,
((.) . (.)) :: (c->r) -> (a->b->c) -> (a->b->r)
((.) . (.)) f g a b = f (g a b)
Indeed,
((.) . (.)) f g a b = ((.) . (.)) f g a b
= (.) ( (.) f) g a b
= ((f .) . g) a b -- NB
= (f .) ( g a) b
= (f . g a) b
= f ( g a b)
NB:
You can also verify that
((.) . (.) . (.)) :: (d->r) -> (a->b->c->d) -> (a->b->c->r)
((.) . (.) . (.)) f g a b c = f (g a b c)
Why? (.)
is fmap
for functions, and so we can re-write the above as
(fmap . fmap . fmap) f g a b c =
fmap (fmap (fmap f)) g a b c =
(do { x <- g ; return $ fmap (fmap f) x }) a b c =
(do { x <- g ; return $ do { y <- x ; return $ fmap f y } }) a b c =
(do { x <- g ; return $ do { y <- x ; return $ do { z <- y ; return $ f z } } }) a b c =
let x=g a in (const $ do { y <- x ; return $ do { z <- y ; return $ f z } }) a b c =
let x=g a in (do { y <- x ; return $ do { z <- y ; return $ f z } }) b c =
let x=g a in let y=x b in (const $ do { z <- y ; return $ f z }) b c =
let x=g a in let y=x b in (do { z <- y ; return $ f z }) c =
let x=g a in let y=x b in let z=y c in const (f z) c =
let x=g a in let y=x b in f (y c) =
let x=g a in f (x b c) =
f (g a b c)
This obviously works for any number of the chained compositions of fmap
s that way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With