Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can applicative functors have side effects, but functors can't?

I'm feeling rather silly asking this question, but it's been on my mind for a while and I can't find any answers.

So the question is: why can applicative functors have side effects, but functors can't?

Maybe they can and I've just never noticed...?

like image 260
Brian Avatar asked Jan 29 '13 05:01

Brian


People also ask

Could you comfortably explain the difference between a monad and an applicative functor?

Functors apply a function to a wrapped value: Applicatives apply a wrapped function to a wrapped value: Monads apply a function that returns a wrapped value to a wrapped value. Monads have a function >>= (pronounced "bind") to do this.

Are all monads applicative functors?

As I understand, every monad is a functor but not every functor is a monad. A functor takes a pure function (and a functorial value) whereas a monad takes a Kleisli arrow, i.e. a function that returns a monad (and a monadic value).

Is applicative a functor?

Applicative functors are the programming equivalent of lax monoidal functors with tensorial strength in category theory. Applicative functors were introduced in 2008 by Conor McBride and Ross Paterson in their paper Applicative programming with effects.

Are functors useful?

Functors are also important because they are a building block for applicatives and monads, which are coming in future posts.


2 Answers

It is not true that Functors don't have effects. Every Applicative (and every Monad through WrappedMonad) is a Functor. The main difference is that Applicative and Monad give you tools how to work with those effects, how to combine them. Roughly

  • Applicative allows you to sequence effects and combine values inside.
  • Monad in addition allows you to determine a next effect according to the result of a previous one.

However Functor only allows you to modify the value inside, it doesn't give tools to do anything with the effect. So if something is just Functor and not Applicative, it doesn't mean it doesn't have effects. It just doesn't have a mechanism how to combine them in this way.

Update: As an example, consider

import Control.Applicative

newtype MyF r a = MyF (IO (r, a))

instance Functor (MyF r) where
    fmap f (MyF x) = MyF $ fmap (fmap f) x

This is clearly a Functor instance that carries effects. It's just that we don't have a way how to define operations with these effects that would comply to Applicative. Unless we impose some additional constraints on r, there is no way how to define an Applicative instance.

like image 126
Petr Avatar answered Nov 07 '22 04:11

Petr


This answer is a bit of an over-simplification, but if we define side effects as computations being affected by previous computations, it's easy to see that the Functor typeclass is insufficient for side effects simply because there is no way to chain multiple computations.

class Functor f where
    fmap :: (a -> b) -> f a -> f b

The only thing a functor can do is to alter the end result of a computation via some pure function a -> b.

However, an applicative functor adds two new functions, pure and <*>.

class Functor f => Applicative f where
    pure   :: a -> f a
    (<*>)  :: f (a -> b) -> f a -> f b

The <*> is the crucial difference here, since it allows us to chain two computations: f (a -> b) (a computation which produces a function) and f a a computation that provides the parameter to which the function is applied. Using pure and <*> it's possible to define e.g.

(*>) :: f a -> f b -> f b

Which simply chains two computations, discarding the end result from the first one (but possibly applying "side effects").

So in short, it's the ability to chain computations which is the minimum requirement for effects such as mutable state in computations.

like image 23
shang Avatar answered Nov 07 '22 04:11

shang