Difference between revisions of "FunctorApplicativeMonad Proposal"
m (→How do programmers need to change their code?: Comment about transitional phase) 
m (Fixed list numbering) 

Line 18:  Line 18:  
# ''Break as little code as possible.'' For example, do not move <hask>return</hask> to Applicative and remove <hask>pure</hask>. Instead, leave <hask>return</hask> in Monad, and give it <hask>pure</hask> as default implementation. 
# ''Break as little code as possible.'' For example, do not move <hask>return</hask> to Applicative and remove <hask>pure</hask>. Instead, leave <hask>return</hask> in Monad, and give it <hask>pure</hask> as default implementation. 

−  
# ''Change only things that are closely related to the proposal.'' For example, using <hask>join</hask> in a monad definition requires it to be a functor, so it goes hand in hand with the AMP. On the other hand, removing <hask>fail</hask> has nothing to do with what we're trying to accomplish. 
# ''Change only things that are closely related to the proposal.'' For example, using <hask>join</hask> in a monad definition requires it to be a functor, so it goes hand in hand with the AMP. On the other hand, removing <hask>fail</hask> has nothing to do with what we're trying to accomplish. 

Line 91:  Line 90:  
# Monads lacking Functor or Applicative instances. This is easily fixable by either setting <hask>fmap = liftM</hask>, <hask>pure = return</hask> and <hask>(<*>) = ap</hask>, although more efficient implementations may exist, or by moving an already existing definition from <hask>Control.Applicative</hask> to the appropriate module. 
# Monads lacking Functor or Applicative instances. This is easily fixable by either setting <hask>fmap = liftM</hask>, <hask>pure = return</hask> and <hask>(<*>) = ap</hask>, although more efficient implementations may exist, or by moving an already existing definition from <hask>Control.Applicative</hask> to the appropriate module. 

−  
# This one is specific to building GHC: importing <hask>Control.Monad/Applicative</hask> introduces a circular module dependency. In this case, one can rely on handwritten implementations of the desired function, e.g. <hask>ap f x = f >>= ...</hask>. 
# This one is specific to building GHC: importing <hask>Control.Monad/Applicative</hask> introduces a circular module dependency. In this case, one can rely on handwritten implementations of the desired function, e.g. <hask>ap f x = f >>= ...</hask>. 

−  
# Libraries using their own <hask>(<*>)</hask>. This one is much tougher, as renaming the operator may require a lot of effort. For building GHC though, this only concerns Hoopl, and a handful of renames. 
# Libraries using their own <hask>(<*>)</hask>. This one is much tougher, as renaming the operator may require a lot of effort. For building GHC though, this only concerns Hoopl, and a handful of renames. 

Revision as of 10:11, 18 June 2013
Haskell calls a couple of historical accidents its own. While some of them, such as the "number classes" hierarchy, can be justified by pragmatism or lack of a strictly better suggestion, there is one thing that stands out as, well, not that: Applicative not being a superclass of Monad.
The topic has been discussed multiple times in the past (cf. link section at the bottom). This article was updated to describe the current, and very likely to succeed, Haskell 2014 Applicative => Monad proposal (AMP).
The initial text of the Haskell 2014 AMP can be found here, and here's the mailing list discussion of the proposal.
Contents
Proposal contents
The list of changes is as follows:
 Applicative becomes a superclass of Monad, and is added to the Prelude.
 Alternative becomes a superclass of MonadPlus (in addition to Monad, of course).

join
is promoted into the Monad typeclass.
The general rationale behind these changes:
 Break as little code as possible. For example, do not move
return
to Applicative and removepure
. Instead, leavereturn
in Monad, and give itpure
as default implementation.  Change only things that are closely related to the proposal. For example, using
join
in a monad definition requires it to be a functor, so it goes hand in hand with the AMP. On the other hand, removingfail
has nothing to do with what we're trying to accomplish.
How do programmers need to change their code?
The following is a list of things you may have to change in your code so the AMP doesn't break it.
 Add Applicative/Functor instances for all your Monads. If you don't care about efficiency, you can simply derive these instances from the Monad by adding the following code:
 Monad m
import Control.Monad (liftM, ap)
import Control.Applicative (Applicative(..))
instance Functor m where
fmap = liftM
instance Applicative m where
pure = return
(<*>) = ap
 Add an Alternative instance for all instances of MonadZero. This can again be done easily using
 MonadZero m
import Control.Monad (mzero, mplus)
import Control.Applicative (Alternative(..))
instance Alternative m where
(<>) = mplus
empty = mzero
 Change your API to not define functions named
<*>
,join
orpure
.
Future versions of GHC will issue warnings if code doesn't comply to these rules; there will be a long enough transitional phase so Hackage can adapt to the AMP in advance before the above mentioned changes are actually enforced.
Discussion and consequences
It's the right thing to do™
Math. You've all heard this one, it's good and compelling so I don't need to spell it out.
Redundant functions

pure
andreturn
do the same thing. 
>>
and*>
are identical. 
liftM
andliftA
arefmap
. TheliftM*
areliftA*
,<*>
isap
.  Prelude's
sequence
requresMonad
right now, whileApplicative
is sufficient to implement it. The more general version of this issue is captured byData.Traversable
, whose main typeclass implements the *same* functionality twice, namelytraverse
andmapM
, andsequenceA
andsequence
.  The
WrappedMonad
type fromControl.Applicative
provides a semiautomatic way to using Functor/Applicative/Alternative functions for Monad/MonadPlus instances as a makeshift patch.
That very much violates the "don't repeat yourself" principle, and even more so it forces the programmer to repeat himself to achieve maximal generality. It may be too late to take all redundancies out, but at least we can prevent new ones from being created.
(Note that it is not proposed to remove any functions for compatibility reasons. Maybe some of them can be phased out in the long run, but that's beyond scope here.)
Using Functor/Applicative functions in monadic code
Whenever there's Monad code, you can use Functor/Applicative functions, without introducing an additional constraint. Keep in mind that "Functor/Applicative functions" does not only include what their typeclasses define but many more, for example void
, (<$>)
, (<**>)
.
Even if you think you have monadic code, strictly using the least restrictive functions may result in something that requires only Applicative. This is similar to writing a function that needs Int
, but it turns out any Integral
will do  more polymorphism for free.
Compatibility issues
These are the kinds of issues to be expected:
 Monads lacking Functor or Applicative instances. This is easily fixable by either setting
fmap = liftM
,pure = return
and(<*>) = ap
, although more efficient implementations may exist, or by moving an already existing definition fromControl.Applicative
to the appropriate module.  This one is specific to building GHC: importing
Control.Monad/Applicative
introduces a circular module dependency. In this case, one can rely on handwritten implementations of the desired function, e.g.ap f x = f >>= ...
.  Libraries using their own
(<*>)
. This one is much tougher, as renaming the operator may require a lot of effort. For building GHC though, this only concerns Hoopl, and a handful of renames.
Beginner friendliness
How often did you say ...
 "A Monad is always an Applicative but due to historical reasons it's not but you can easily verify it by setting
pure = return
and(<*>) = ap
"  "
liftM
isfmap
but not really."  "So when should I usefmap
and whenliftM
?"  *sigh*
With the new hierarchy, the answer would *always* be "use the least restrictive one".
Applying the AMP to GHC and then Haskell in practice
Proposed is a gradual introduction of the AMP in three phases:
Prepare GHC
Using a GHC fork with the full patch applied, find and fix all compilation errors introduced by the change by adding Functor/Applicative instances for all Monads.
According to SPJ, adding an adhoc warning of sorts "Monad without Applicative detected" is not a problem, which will be crucial for the next phase. More specifically, issue a warning if:
 Monad without Applicative
 MonadPlus without Alternative
 One of <*>
, pure
, join
is defined in a different context to avoid naming conflicts, as these functions will go into the Prelude
Prepare Hackage
The warning just mentioned will hint to all authors that they should fix (or help others fix) the noncomplying packages. This will ideally lead to libraries eventually adding Applicative instances, and changing their APIs if they redefine operators like <*>
.
After enough time has passed by so libraries adapted to the circumstances, move on to the next phase.
Apply the proposal
Once Hackage is prepared, applying the changes to the Base package is painless. However, this is not primarily a GHC, but a Haskell change. The previous steps were basically preparing the landscape, and when we've (hopefully) found out that it is a good idea to go through with it, it can be proposed to go into the Report. If we make it this far, the AMP should pass quite easily.
Previous proposals
 From early 2011: GHC ticket – Makes Applicative into a superclass of Monad, but does not deprecate any existing names
 See [1] for the associated discussion.
 The Other Prelude