4

So I found out that fmap, a Functor function can be expressed in terms of Monadic operator >>= and return function like this:

fmap' :: (Monad m) => (a -> b) -> m a -> m b
fmap' g x = x >>= (\y ->
                return (g y))

So my first question is how can we implement return function based on fmap?

Also if we can implement return function based on fmap, can we reduce Haskell expressions in do blocks? Would that produce more elegant code?

For instance:

Just x -> do 
    y <- f x
    return (a:y)
4
  • 4
    ...No, this isn't doable. Being a monad is a strictly stronger claim than being a functor. A monad can always be treated as a functor, but the converse is false. Commented Nov 6, 2013 at 0:09
  • 3
    A Functor doesn't have a way to create a wrapped value - fmap can only transform a wrapped value to another wrapped value. To create a wrapped value you need something more powerful like an Applicative where you have pure :: a -> f a. Commented Nov 6, 2013 at 0:21
  • 2
    And to answer your other question: yes, you can make that code more compact. do { y <- f x; return (a:y) } is the same as fmap (a:) (f x) (can you prove it? hint: use your equation for fmap'). Commented Nov 6, 2013 at 3:13
  • 2
    Sort of tangential, but there's a well-known function join :: Monad m => m (m a) -> m a. join can be written in terms of >>= and id, and >>= can be written in terms of join and fmap. You might be interested to work out how. Commented Nov 6, 2013 at 8:24

2 Answers 2

3

We cannot generally implement return in terms of fmap. Monad is just more powerful than Functor.

As an exercise, however, we can try and ask this question: what second operation, if any, would make it possible to implement return in turns of fmap? We can attack this question by looking at the types. (We'll use pure from the Applicative class instead of return—they're basically the same operation.)

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

Well, one possible way this could go is if we have the following function:

-- This is like the standard `const` function, but restricted to the `()` type:
const' :: a -> () -> a
const' a = \() -> a

Then we can write this, which is "almost" pure/return:

almostThere :: Functor f => a -> f () -> f a
almostThere a = fmap (const' a)

And then if we had the following class, we could write pure in terms of it:

class Functor f => Pointed f where
    unit :: f ()

pure :: Pointed f => a -> f a
pure a = almostThere a unit

To make a long story short, what this boils down to is that return, pure and unit all are functions that allow you to make an f from scratch, while fmap only allows you to make an f if you already have another one. There is no way you can use fmap to implement return/pure unless you have access to some third operation that has the "power" to make an f from scratch. The unit operation I showed is probably the simplest one that has this "power."

Sign up to request clarification or add additional context in comments.

Comments

2

The simplest example of a functor where return is just impossible is (,) a:

instance Functor ((,) a) where
  fmap f (a, x) = (a, f x)

But to make this a monad, to implement return, you'd need to generate an a value (for any type!) out of thin air. The only way to do it would be return x = (undefined, x), which is hardly a solution...

1 Comment

This is for general a, however. There is a perfectly good Monad instance when a itself is a Monoid - the Writer Monad used to be implemented as a newtype around this, before it was made a synonym for WriterT Identity.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.