FunctorApplicativeMonad Proposal
From HaskellWiki
m (Removed invalid ticket link) 
(Join will not be part of Monad for now, strike element off the list) 

(9 intermediate revisions by 3 users not shown)  
Line 1:  Line 1:  
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. 
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 topic has been discussed multiple times in the past (cf. link section at the very end). '''This article was updated to describe the current, and very likely to succeed, Haskell 2014 Applicative => Monad proposal (AMP)'''. 
Some relevant links: 
Some relevant links: 

* [https://github.com/quchen/articles/blob/master/applicative_monad.md Initial text of the Haskell 2014 AMP] 
* [https://github.com/quchen/articles/blob/master/applicative_monad.md Initial text of the Haskell 2014 AMP] 

−  * [http://article.gmane.org/gmane.comp.lang.haskell.libraries/19482 AMP mailing list discussion] 
+  * [http://thread.gmane.org/gmane.comp.lang.haskell.libraries/19482 AMP mailing list discussion] 
* Phase one: ticket [http://hackage.haskell.org/trac/ghc/ticket/8004 #8004] 
* Phase one: ticket [http://hackage.haskell.org/trac/ghc/ticket/8004 #8004] 

Line 16:  Line 16:  
# Applicative becomes a superclass of Monad, and is added to the Prelude. 
# Applicative becomes a superclass of Monad, and is added to the Prelude. 

# Alternative becomes a superclass of MonadPlus (in addition to Monad, of course). 
# Alternative becomes a superclass of MonadPlus (in addition to Monad, of course). 

−  # <hask>join</hask> is promoted into the Monad typeclass. 
+  # <s><hask>join</hask> is promoted into the Monad typeclass.</s>  Left out due to nontrivial breaking interaction with [[Roles]]. Will "probably be fixed in the future", but for now it's off the table. 
The general rationale behind these changes: 
The general rationale behind these changes: 

Line 25:  Line 25:  
−  == How do programmers need to change their code? == 
+  == Futureproofing current code == 
−  The following is a list of things you may have to change in your code so the AMP doesn't break it. 
+  GHC 7.8 will issue two types of warnings in order to encourage widescale code fixing. The following describes how to get rid of them, and as a result ensures your code builds both now and after the AMP is finished. 
−  * 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: 
+  === Missing superclasses === 
−  <haskell> 

−   Monad m 

−  import Control.Monad (liftM, ap) 
+  (Warnings of the type "Warning: X is an instance of C, but not D") 
+  
+  * Add Applicative/Functor instances for all your Monads. You can simply derive these instances from the Monad by adding the following code: 

+  <haskell> 

import Control.Applicative (Applicative(..)) 
import Control.Applicative (Applicative(..)) 

+  import Control.Monad (liftM, ap) 

+  
+   Monad m 

instance Functor m where 
instance Functor m where 

Line 42:  Line 45:  
</haskell> 
</haskell> 

−  * Add an Alternative instance for all instances of MonadZero. This can again be done easily using 
+  * Add an Alternative instance for all instances of MonadPlus. This can again be done easily using 
<haskell> 
<haskell> 

−   MonadZero m 

−  
−  import Control.Monad (mzero, mplus) 

import Control.Applicative (Alternative(..)) 
import Control.Applicative (Alternative(..)) 

+  import Control.Monad (mzero, mplus) 

+  
+   MonadPlus m 

instance Alternative m where 
instance Alternative m where 

Line 54:  Line 57:  
</haskell> 
</haskell> 

−  * Change your API to not define functions named <hask><*></hask>, <hask>join</hask> or <hask>pure</hask>. 
+  === Future Prelude names === 
−  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. 
+  "The name X clashes with a future Prelude name"  Prelude will export functions called <hask><*></hask>, <hask>join</hask> and <hask>pure</hask>, so if a module defines its own versions of them, there will be name clashes. There are multiple ways of getting rid of this type of warning (in a futureproof way). 
+  
+  # Change your code to not define functions named <hask><*></hask>, <hask>join</hask> or <hask>pure</hask>. 

+  # Import Prelude definitions you need explicitly. For example, <hask>import Prelude (map, (+))</hask> would not import <hask>join</hask>, so no warning is issued as the module is compatible with the Prelude exporting <hask>join</hask>. <hask>hiding</hask>. 

+  # Due to GHC internals, you cannot use <hask>hiding (join, (<*>), pure)</hask> to silence the warnings, although this method would be futureproof. If you want to use <hask>hiding</hask>, you will have to silence the warnings using a sledgehammer <code>fnowarnamp</code> compiler flag. (If you do so make sure you know what you're doing, otherwise your module ''will'' break in 7.10.) To make 7.10 not complain about the then unrecognized flag, it's best to specify it in a CPP block, 

+  <haskell> 

+  {# LANGUAGE CPP #} 

+  #if __GLASGOW_HASKELL__ >= 707 && __GLASGOW_HASKELL__ < 710 

+  {# OPTIONS_GHC fnowarnamp #} 

+  #endif 

+  </haskell> 

== Discussion and consequences == 
== Discussion and consequences == 

Line 101:  Line 104:  
* "A Monad is always an Applicative but due to historical reasons it's not but you can easily verify it by setting <hask>pure = return</hask> and <hask>(<*>) = ap</hask>" 
* "A Monad is always an Applicative but due to historical reasons it's not but you can easily verify it by setting <hask>pure = return</hask> and <hask>(<*>) = ap</hask>" 

−  * "<hask>liftM</hask> is <hask>fmap</hask> but not really."  "So when should I use <hask>fmap</hask> and when <hask>liftM</hask>?"  *sigh* 
+  * "<hask>liftM</hask> is <hask>fmap</hask> but not really."  "So when should I use <hask>fmap</hask> and when <hask>liftM</hask>?"  ''sigh'' 
−  
−  With the new hierarchy, the answer would *always* be "use the least restrictive one". 

−  
+  With the new hierarchy, the answer would ''always'' be "use the least restrictive one". 

== Applying the AMP to GHC and then Haskell in practice == 
== Applying the AMP to GHC and then Haskell in practice == 

Line 109:  Line 113:  
−  === Prepare GHC === 
+  === '''Done''': 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. 
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. 

Line 119:  Line 123:  
* One of <hask><*></hask>, <hask>pure</hask>, <hask>join</hask> is defined in a different context to avoid naming conflicts, as these functions will go into the Prelude 
* One of <hask><*></hask>, <hask>pure</hask>, <hask>join</hask> is defined in a different context to avoid naming conflicts, as these functions will go into the Prelude 

−  +  === '''Current stage''': Prepare Hackage === 

−  === 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 <hask><*></hask>. 
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 <hask><*></hask>. 
Revision as of 21:47, 8 July 2014
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 very end). This article was updated to describe the current, and very likely to succeed, Haskell 2014 Applicative => Monad proposal (AMP).
Some relevant links:
 Initial text of the Haskell 2014 AMP
 AMP mailing list discussion
 Phase one: ticket #8004
Contents 
1 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).

 Left out due to nontrivial breaking interaction with Roles. Will "probably be fixed in the future", but for now it's off the table.is promoted into the Monad typeclass.join
The general rationale behind these changes:
 Break as little code as possible. For example, do not move to Applicative and removereturn. Instead, leavepurein Monad, and give itreturnas default implementation.pure
 Change only things that are closely related to the proposal. For example, using in a monad definition requires it to be a functor, so it goes hand in hand with the AMP. On the other hand, removingjoinhas nothing to do with what we're trying to accomplish.fail
2 Futureproofing current code
GHC 7.8 will issue two types of warnings in order to encourage widescale code fixing. The following describes how to get rid of them, and as a result ensures your code builds both now and after the AMP is finished.
2.1 Missing superclasses
(Warnings of the type "Warning: X is an instance of C, but not D")
 Add Applicative/Functor instances for all your Monads. You can simply derive these instances from the Monad by adding the following code:
import Control.Applicative (Applicative(..)) import Control.Monad (liftM, ap)  Monad m instance Functor m where fmap = liftM instance Applicative m where pure = return (<*>) = ap
 Add an Alternative instance for all instances of MonadPlus. This can again be done easily using
import Control.Applicative (Alternative(..)) import Control.Monad (mzero, mplus)  MonadPlus m instance Alternative m where (<>) = mplus empty = mzero
2.2 Future Prelude names
"The name X clashes with a future Prelude name"  Prelude will export functions called Change your code to not define functions named ,<*>orjoin.pure
 Import Prelude definitions you need explicitly. For example, would not importimport Prelude (map, (+)), so no warning is issued as the module is compatible with the Prelude exportingjoin.join.hiding
 Due to GHC internals, you cannot use to silence the warnings, although this method would be futureproof. If you want to usehiding (join, (<*>), pure), you will have to silence the warnings using a sledgehammerhiding
fnowarnamp
compiler flag. (If you do so make sure you know what you're doing, otherwise your module will break in 7.10.) To make 7.10 not complain about the then unrecognized flag, it's best to specify it in a CPP block,
{# LANGUAGE CPP #} #if __GLASGOW_HASKELL__ >= 707 && __GLASGOW_HASKELL__ < 710 {# OPTIONS_GHC fnowarnamp #} #endif
3 Discussion and consequences
3.1 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.
3.2 Redundant functions
 andpuredo the same thing.return
 and>>are identical.*>
 andliftMareliftA. ThefmapareliftM*,liftA*is<*>.ap
 Prelude's requressequenceright now, whileMonadis sufficient to implement it. The more general version of this issue is captured byApplicative, whose main typeclass implements the *same* functionality twice, namelyData.Traversableandtraverse, andmapMandsequenceA.sequence
 The type fromWrappedMonadprovides a semiautomatic way to using Functor/Applicative/Alternative functions for Monad/MonadPlus instances as a makeshift patch.Control.Applicative
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.)
3.3 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
3.4 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 = liftMandpure = return, although more efficient implementations may exist, or by moving an already existing definition from(<*>) = apto the appropriate module.Control.Applicative
 This one is specific to building GHC: importing introduces a circular module dependency. In this case, one can rely on handwritten implementations of the desired function, e.g.Control.Monad/Applicative.ap f x = f >>= ...
 Libraries using their own . This one is potentially the most laborious consequence. For building GHC though, this only concerns Hoopl, and a handful of renames.(<*>)
3.5 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 andpure = return"(<*>) = ap
 "isliftMbut not really."  "So when should I usefmapand whenfmap?"  sighliftM
With the new hierarchy, the answer would always be "use the least restrictive one".
4 Applying the AMP to GHC and then Haskell in practice
Proposed is a gradual introduction of the AMP in three phases:
4.1 Done: 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 ,<*>,pureis defined in a different context to avoid naming conflicts, as these functions will go into the Preludejoin
4.2 Current stage: 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 likeAfter enough time has passed by so libraries adapted to the circumstances, move on to the next phase.
4.3 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.
5 Previous proposals
 Early 2011: GHC ticket – changes similar to this proposal, but closed as "not GHC, but Haskell". See here for the associated discussion.
 The Other Prelude