Personal tools

Functor-Applicative-Monad Proposal

From HaskellWiki

(Difference between revisions)
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.

Contents

1 Make
Applicative
a superclass of
Monad

class Applicative m => Monad m where
    ...

1.1 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.

1.2 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.

2 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

2.1 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.

2.2 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.

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

3.1 For

  • We will end up with a simpler base library.

3.2 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.

4 Split
fail
into its own class

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

5 Rename
fmap
to
map

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

6 Export
Applicative
in the Prelude

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

8 Add a
Pointed
class

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

This is already implemented in the pointed package.

8.1 For

8.2 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.

9 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.