Difference between revisions of "Functor-Applicative-Monad Proposal"

From HaskellWiki
Jump to: navigation, search
(Rewrite the page!)
(Add more things!)
Line 35: Line 35:
 
=== For ===
 
=== For ===
   
* <hask>fmap</hask>/<hask>join</hask> is more orthogonal than <hask>fmap</hask>/<hask>>>=</hask>, and the former is closer to the categorical definition.
+
* <hask>fmap</hask>/<hask>join</hask> is more orthogonal, and is closer to the categorical definition.
   
 
* <hask>join</hask> is often easier to implement. See [http://article.gmane.org/gmane.comp.lang.haskell.libraries/14926].
 
* <hask>join</hask> is often easier to implement. See [http://article.gmane.org/gmane.comp.lang.haskell.libraries/14926].
Line 45: Line 45:
 
* <hask>>>=</hask> is used much more frequently in real-world code than <hask>join</hask>.
 
* <hask>>>=</hask> is used much more frequently in real-world code than <hask>join</hask>.
   
* Performance: The default implementation of <hask>>>=</hask> requires two traversals. Any container-like type which only implements <hask>fmap</hask> and <hask>join</hask> would be slower.
+
* Performance: The default implementation of <hask>>>=</hask> requires two traversals. A container-like type which only implements <hask>join</hask> would most likely be slower.
   
 
== Remove <hask>liftM</hask>, <hask>ap</hask>, etc. in favor of their Applicative counterparts ==
 
== Remove <hask>liftM</hask>, <hask>ap</hask>, etc. in favor of their Applicative counterparts ==
Line 55: Line 55:
 
=== Against ===
 
=== Against ===
   
* A lot of code will be broken by this change. There is no compelling reason to remove these functions outright, rather than gradually deprecating them as with <hask>Prelude.catch</hask>.
+
* A lot of code will be broken by this change. Of course, we can gradually deprecate them as with <hask>Prelude.catch</hask>.
   
* A common pattern is to write a full instance of Monad, then set <hask>fmap = liftM</hask> and <hask>(<*>) = ap</hask>.
+
* A common pattern is to write a full instance of Monad, then set <hask>fmap = liftM</hask> and <hask>(<*>) = ap</hask>. The functions are still useful for this purpose.
   
 
== Split <hask>fail</hask> into its own class ==
 
== Split <hask>fail</hask> into its own class ==
Line 67: Line 67:
   
 
== Rename <hask>fmap</hask> to <hask>map</hask> ==
 
== Rename <hask>fmap</hask> to <hask>map</hask> ==
  +
  +
<haskell>
  +
class Functor f where
  +
map :: (a -> b) -> f a -> f b
  +
</haskell>
   
 
== Export <hask>Applicative</hask> in the Prelude ==
 
== Export <hask>Applicative</hask> in the Prelude ==

Revision as of 02:09, 3 June 2013

The standard class hierarchy is a consequence of Haskell's historical development, rather than logic.

This article attempts to document various suggestions that have been brought up over the years, along with arguments for and against.

Make Applicative a superclass of Monad

class Applicative m => Monad m where
    ...

For

  • Code that is polymorphic over the Monad can use Applicative operators rather than the ugly liftM and ap.
  • Most types that implement Monad also implement Applicative already. This change will only make explicit a current best practice.

Against

  • Monad is part of standard Haskell, but Applicative is not. If Monad is made a subclass of Applicative, then we will need to add Applicative to the language standard.
  • Some libraries, such as blaze-markup, only implement Monad for its do-notation. For these types, an Applicative instance would have no meaning.

Add join as a method of Monad

class Applicative m => Monad m where
    (>>=) :: (a -> m b) -> m a -> m b
    join :: m (m a) -> m a
    ...
    m >>= k = join (fmap k m)
    join m = m >>= id

For

  • fmap/join is more orthogonal, and is closer to the categorical definition.
  • join is often easier to implement. See [1].
  • The analogous comonad package is written this way.

Against

  • >>= is used much more frequently in real-world code than join.
  • Performance: The default implementation of >>= requires two traversals. A container-like type which only implements join would most likely be slower.

Remove liftM, ap, etc. in favor of their Applicative counterparts

For

  • We will end up with a simpler base library.

Against

  • A lot of code will be broken by this change. Of course, we can gradually deprecate them as with Prelude.catch.
  • A common pattern is to write a full instance of Monad, then set fmap = liftM and (<*>) = ap. The functions are still useful for this purpose.

Split fail into its own class

class Monad m => MonadFail m where
    fail :: String -> m a

Rename fmap to map

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

Export Applicative in the Prelude

Redefine >> in terms of *> rather than >>=

Add a Pointed class

class Pointed p where
    point :: a -> p a

This is already implemented in the pointed package.

For

Against

  • This class has seen little real-world use. On Hackage, there are only 9 reverse dependencies for pointed, most of which are by the same author.

Related proposals

  • From early 2011: GHC ticket – Makes Applicative into a superclass of Monad, but does not deprecate any existing names
    • See [2] for the associated discussion.
  • The Other Prelude

Context alias would also be a great help with backwards compatibility. The class system extension proposal may also help.