I known that r -> a
is a Functor
in a
, and that fmap = (.)
for it.
This means that when I do fmap f g
, with g :: r -> a
, f
is applied to the result of g
as soon as the latter is fed with a value of type r
. However, if a
is a function type, e.g. a unary function b -> c
, then there's a difference between applying f
to that function (which is what happens in reality) and applying f
to the eventual result of g
(which could be desirable in some cases; couldn't it?).
How do I map on the eventual result of g
? In this case of g :: r -> b -> c
it seems easy, I can just uncurry $ fmap f $ curry g
. But what if also c
is a function type?
Or, in other words, how do I map on the final result of multi-variable function? Is the curry
/uncurry
trick necessary/doable in general?
(Related question here.)
As it's apparent from comments/answers, I have not realized I was essentially asking the same question I've already asked some days ago. Probably it was not apparent to me because of how I got here. Essentially, what led me to ask this question is another one storming in my head:
If I can use liftA2 (&&)
, liftA2 (||)
, and similar to combine unary predicates, how do I combine binary predicates? To answer to this, I played around a bit in GHCi, and came up with this
liftIntoBinaryFunc p = \q r -> curry $ (liftA2 p) (uncurry q) (uncurry r)
which would allow doing something like this:
-- some binary predicate
distanceLE3 x y = 3 >= abs (x - y)
sameSign x y = ((==) `on` signum) x y
-- function to lift into binary functions
liftIntoBinaryFunc p = \q r -> curry $ (liftA2 p) (uncurry q) (uncurry r)
-- lifting && into binary functions to Bool
and' = liftIntoBinaryFunc (&&)
-- combining
pred = distance3 `and'` sameSign
-- using
p 3 18 -- False
p 3 1 -- True
p 3 (-1) -- False
However, this question too generalizes to how do I combine predicates of arbitrary (though common) arity?, and probably the answer is the same.
r -> b -> c
is r -> (b -> c)
and so (fmap . fmap) (f :: c -> d) (g :: r -> b -> c) :: r -> b -> d
:
> foo :: (c -> d) -> (r -> (b -> c)) -> r -> (b -> d) ;
foo = fmap . fmap
foo :: (c -> d) -> (r -> b -> c) -> r -> b -> d
> bar :: (c -> d) -> (r -> (b -> (t -> c))) -> r -> (b -> (t -> d)) ;
bar = fmap . fmap . fmap
bar :: (c -> d) -> (r -> b -> t -> c) -> r -> b -> t -> d
But you will have to know how many nested levels are there, to compose the fmaps
accordingly, or use approaches from your other recent question.