($) is to (.) as `fmap` is to?

404 Views Asked by At

I have a function funcM :: a -> b -> c -> IO (x, y)

I want to write a function funcM_ :: a-> b-> c-> IO x so:

funcM_ = fst `fmap` funcM -- error

I could add back all the points, but it seems like there should be something I could replace fmap with so that the above will work. Kind of like replacing ($) with (.) would make this work in a pure context.

What is the function I am looking for?

3

There are 3 best solutions below

3
On

Take a look at the following answer: https://stackoverflow.com/a/20279307/783743 It explains how to convert your code into pointfree style. Let's start with a non-pointfree definition of funcM_:

funcM_ a b c = fmap fst (funcM a b c)

-- But `\x -> f (g x)` is `f . g`. Hence:

funcM_ a b = fmap fst . (funcM a b)

-- But `\x -> f (g x)` is `f . g`. Hence:

funcM_ a = (fmap fst .) . (funcM a)

-- But `\x -> f (g x)` is `f . g`. Hence:

funcM_ = ((fmap fst .) .) . funcM

Another way to do this would be to use uncurry and curry as follows:

uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 f (a, b, c) = f a b c

curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry3 f a b c = f (a, b, c)

(.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
f .:: g = curry3 (f . (uncurry3 g))

Now you can write funcM_ as follows:

funcM_ = fmap fst .:: funcM

You could also write .:: in pointfree style as follows:

(.::) :: (d -> e) -> (a -> b -> c -> d) -> a -> b -> c -> e
(.::) = (.) . (.) . (.)

Hope that helped.

0
On

Add a dot for each argument to funcM

These are all equivalent:

((fmap fst . ) .) . funcM
((.)  . (.)  . (.))  (fmap fst) funcM
(fmap . fmap . fmap) (fmap fst) funcM

import Data.Functor.Syntax -- from 'functors' package
(.::) (fmap fst) funcM

Note that all I did was change the implicit ($) to (.). :-)

(.) is the implementation of fmap for the function instance of Functor :

instance Functor ((->) a) b where
  fmap f g = f . g

GHCi :t is your friend.

1
On

Since you’re composing a one-argument function (fmap) with a three-argument function (funcM), you need three levels of composition:

funcM_ = ((fmap fst .) .) . funcM

This is equivalent to the pointed version by a simple expansion:

funcM_ x = (fmap fst .) . funcM x
funcM_ x y = fmap fst . funcM x y
funcM_ x y z = fmap fst (funcM x y z)

This follows from the type of fmap, really:

fmap :: (Functor f) => (a -> b) -> f a -> f b

You’re just partially applying the arguments to funcM so that you have an f a (here IO (x, y)) which you give to fmap fst to get back an f b (IO x).

As an aside, M_ usually implies returning m ().