Difference between revisions of "Merely monadic"

From HaskellWiki
Jump to navigation Jump to search
(Comment about monad laws relocated from 'Monad')
m (Incorrect link replaced #-:<)
 
(3 intermediate revisions by the same user not shown)
Line 1: Line 1:
  +
== Introduction ==
'''''Monads''''' in Haskell can be thought of as ''composable'' computation descriptions. The essence of monad is thus ''separation'' of ''composition timeline'' from the composed computation's ''execution timeline'', as well as the ability of ''computation'' to implicitly carry extra data, as pertaining to the computation itself, in addition to its ''one'' (hence the name) output, that it '''''will produce''''' when run (or queried, or called upon). This lends monads to supplementing ''pure'' calculations with features like I/O, common environment, updatable state, etc.
 
   
  +
In Haskell, ''monadic types'' - types having an instance for the <code>Monad</code> class - can be thought of as ''abstract descriptors'' of ''computations'' which are inherently ''composable'' - smaller monadic expressions (''actions'') can be used to build larger ones.
Each monad, or computation type, provides means, subject to '''''Monad Laws''''', to
 
   
  +
This ''monadic interface'' (as specified by <code>Monad</code>) provides actions additional flexibility in separating:
* '''''(a)''''' ''create'' a description of a computation that will produce (a.k.a. "return") a given Haskell value, and
 
   
  +
* the ''time of composition'': when it is defined;
* '''''(b)''''' ''combine'' (a.k.a. "bind") a computation description with a ''reaction'' to it, &ndash; a pure Haskell function that is set to receive a computation-produced value (when and if ''that'' happens) and return another computation description, using or dependent on that value if need be, &ndash; creating a description of a combined computation that will feed the original computation's output through the reaction while automatically taking care of the particulars of the computational process itself.
 
   
  +
* the ''time of execution'': when it is used;
''Reactions'' are thus computation description ''constructors''. A monad might also define additional primitives to provide access to and/or enable manipulation of data it implicitly carries, specific to its nature; cause some specific side-effects; etc. As for the [[Monad laws|monad laws]], they basically say that a monad's reactions should be associative under Kleisli composition, defined as <code>(f >=> g) x = f x >>= g</code>, with <code>return</code> its left and right identity element.
 
   
  +
* the ''mode of computation'': what else it can do, in addition to emitting its ''single'' (hence the name) output value.
Sometimes the specific monadic type also provides the ability to somehow '''''(c)''''' ''run'' a computation description, getting its result back into Haskell if computations described by the monad are pure, but this is expressly '''''not''''' a part of the Monad interface. Officially, <i>you can't get the <hask>a</hask> out of <hask>M a</hask></i> directly, only arrange for it to be "fed" into the next computation's constructor, the "reaction", indirectly. In case of an <hask>IO</hask> monad value, a computation it describes runs implicitly as a part of the chain of I/O computation descriptions composed together into the value <hask>main</hask> (of type <hask>IO ()</hask>) in a given Haskell program, by convention. <!-- Put simply, it runs when the compiled program runs (but then, everything does). -->
 
   
  +
== Monadic operations ==
<haskell style="background-color:#f8f1ab;border-radius:15px;border:2px solid #000000;padding:15px">
 
# Monad interactions:
 
   
  +
Ideally, each monadic type in Haskell should satisfy the ''[[Monad Laws|monad laws]]'' by providing two basic operations:
(a) reaction $ value ==> computation_description
 
   
  +
* <code>return :: Monad m => a -> m a</code>:
(b) reaction =<< computation_description ==> computation_description
 
  +
:{|
  +
| argument
  +
| <code>:: a</code>
  +
| another Haskell value
  +
|-
  +
| result
  +
| <code>:: m a</code>
  +
| an action, merely ''returning'' the argument's value.
  +
|}
   
  +
* <code>(>>=) :: Monad m => m a -> (a -> m b) -> m b</code>:
(c) reaction $ computation_description ==> ***type_mismatch***
 
  +
:{|
  +
| argument #1
  +
| <code>:: m a</code>
  +
| an action
  +
|-
  +
| argument #2
  +
| <code>:: (a -> m b)</code>
  +
| a suitable Haskell function (a ''reaction'')
  +
|-
  +
| result
  +
| <code>:: m b</code>
  +
| <p>another action, the result of ''binding'' the output value of argument #1<br>
  +
(the action) to the input of argument #2 (the reaction).</p>
  +
|}
  +
Together, these two operations allow many of the specific (and often-repetitive) details of the computational processes used by actions to be abstracted away. This often makes the monadic interface the preferred choice for ''abstract data types'', where the inner workings of monadic actions must be kept private e.g. to ensure they work as intended.
   
  +
As a reaction uses its argument to build a new monadic result, it can be thought of as an abstract ''constructor'' for new actions. In this way <code>return</code> can then be seen as the trivial-action constructor for new actions: the monadic counterpart to the identity function <code>Prelude.id</code>:
(d) reaction <$> computation_description ==> computation_description_description
 
   
  +
<haskell>
(e) join $ computation_description_description ==> computation_description
 
  +
id :: a -> a
  +
id x = x
 
</haskell>
 
</haskell>
   
  +
=== <code>IO</code>, in particular ===
(<i><hask>join</hask></i> is another function expressing the essence of monad; where <hask>m >>= k = k =<< m = join (k <$> m) = join (fmap k m)</hask>; it is prefered in mathematics, over the ''bind''; both express the same concept).
 
   
  +
The <code>IO</code> type is special because a Haskell program is one large <code>IO</code> action (usually) called <code>main</code> - for any other <code>IO</code> action to work, it should be part of the chain of I/O actions composed together to form <code>main</code>. Execution of a Haskell program then starts by the Haskell implementation calling <code>main</code>, according to the implementation's private definition of the abstract <code>IO</code> type.
Thus in Haskell, though it is a purely-functional language, side effects that '''''will be performed''''' by a computation can be dealt with and combined ''purely'' at the monad's composition time. Monads thus resemble programs in a particular [[EDSL]] (''embedded'' domain-specific language, "embedded" because the values denoting these computations are legal Haskell values, not some extraneous annotations).
 
   
  +
== Using monadic actions ==
While programs may describe impure effects and actions ''outside'' Haskell, they can still be combined and processed (''"assembled"'') purely, ''inside'' Haskell, creating a pure Haskell value - a computation action description that describes an impure calculation. That is how Monads in Haskell help keep the ''pure'' and the ''impure'' apart.
 
   
  +
Assuming:
The computation doesn't have to be impure and can be pure itself as well. Then monads serve to provide the benefits of separation of concerns, and automatic creation of a computational "pipeline".
 
  +
  +
* <code>M</code> is a monadic type;
  +
  +
* <code>x :: a</code> is some other Haskell value;
  +
  +
* <code>m :: M a</code> is an action;
  +
  +
* <code>k :: (a -> M b)</code> is a reaction;
  +
  +
* <code>M</code>, <code>x</code>, <code>m</code>, <code>k</code> are ''well-behaved'';
  +
  +
then:
  +
  +
{|
  +
| (a)
  +
| <code>r1 = k $ x</code>
  +
| <code> :: M b</code>
  +
| (''a new action'')
  +
|-
  +
| (b)
  +
| <code>r2 = return x</code>
  +
| <code> :: M a</code>
  +
| (''an action whose output value is'' <code>x</code>)
  +
|-
  +
| (c)
  +
| <code>r3 = m >>= k</code>
  +
| <code> :: M b</code>
  +
| (''another new action'')
  +
|-
  +
| (d)
  +
| <code>r4 = k =<< m</code>
  +
| <code> :: M b</code>
  +
| (''the same as'' <code>r3</code>)
  +
|-
  +
| (e)
  +
| <code>r5 = k $ m</code>
  +
| '''(''types don't match'')'''
  +
|
  +
|-
  +
| (f)
  +
| <code>r6 = k <$> m</code>
  +
| <code> :: M (M b)</code>
  +
| (''an action whose output value is also the same as'' <code>r3</code>)
  +
|-
  +
| (g)
  +
| <code>r7 = join (k <$> m)</code>
  +
| <code> :: M b</code>
  +
| (''the same as'' <code>r3</code>)
  +
|}
  +
  +
where:
  +
  +
<haskell>
  +
infixr 0 $
  +
($) :: (a -> b) -> a -> b
  +
f $ x = f x
  +
  +
infixr 1 =<<
  +
(=<<) :: (a -> M b) -> M a -> M b
  +
k =<< m = m >>= k
  +
  +
infixl 4 <$>
  +
(<$>) :: (a -> b) -> M a -> M b
  +
f <$> m = m >>= \x -> return (f x)
  +
  +
join :: M (M a) -> M a
  +
join m = m >>= \x -> x
  +
</haskell>
  +
  +
Even though it is a purely-functional language, Haskell can facilitate the use of effect-based computations by presenting them as monadic actions (using a suitable type), with the monadic interface helping to maintain purity. This is the result of the aformentioned separation of concerns, most notably the ''time of composition''. Monadic actions thus resemble programs in a particular [[EDSL]] (''embedded'' domain-specific language), "embedded" because they are legitimate Haskell values which ''describe'' impure computations - no extraneous annotations are needed. This is how monadic types in Haskell help keep the ''pure'' and the ''impure'' apart. But not all monadic actions are impure - for them, the benefits of separating concerns are often combined with the automatic formation of a computational "pipeline".
  +
  +
Because they are very useful in practice but rather confronting for beginners, numerous [[Monad tutorials timeline|monad tutorials]] (including this one) exist to help with the learning process.
  +
  +
== Supplemental operations for monadic types (''non-standard morphisms'') ==
  +
  +
In addition to the two basic ones required by the monadic interface, each monadic type can have its own specific operations e.g. to provide access to and/or enable manipulation of data it implicitly carries which is specific to its nature; cause some specific side-effects; etc.
  +
  +
=== Running monadic actions ===
  +
  +
Some monadic types provide an extra operation for ''running'' actions, usually if they are ''purely'' defined by the type. This allows an action's output value to be obtained directly in Haskell, in contrast to <code>(>>=)</code>. While some of the predefined (e.g. from the <code>Prelude</code>) monadic types in Haskell have such an operation (e.g. <code>Prelude.maybe</code> or <code>Prelude.either</code>), most do not.
  +
  +
Why this is expressly '''''not''''' a part of the monadic interface by now should be obvious - impure monadic actions could then be "hidden" inside seemingly-pure definitions, which would end any notion of purity in Haskell. This is why the only general-purpose way to use a monadic action's output value is to direct it to subsequent computations via the reaction which constructs them, through the use of <code>(>>=)</code>.
   
 
[[Category:Monad]]
 
[[Category:Monad]]

Latest revision as of 04:35, 16 March 2021

Introduction

In Haskell, monadic types - types having an instance for the Monad class - can be thought of as abstract descriptors of computations which are inherently composable - smaller monadic expressions (actions) can be used to build larger ones.

This monadic interface (as specified by Monad) provides actions additional flexibility in separating:

  • the time of composition: when it is defined;
  • the time of execution: when it is used;
  • the mode of computation: what else it can do, in addition to emitting its single (hence the name) output value.

Monadic operations

Ideally, each monadic type in Haskell should satisfy the monad laws by providing two basic operations:

  • return :: Monad m => a -> m a:
argument :: a another Haskell value
result :: m a an action, merely returning the argument's value.
  • (>>=) :: Monad m => m a -> (a -> m b) -> m b:
argument #1 :: m a an action
argument #2 :: (a -> m b) a suitable Haskell function (a reaction)
result :: m b

another action, the result of binding the output value of argument #1
(the action) to the input of argument #2 (the reaction).

Together, these two operations allow many of the specific (and often-repetitive) details of the computational processes used by actions to be abstracted away. This often makes the monadic interface the preferred choice for abstract data types, where the inner workings of monadic actions must be kept private e.g. to ensure they work as intended.

As a reaction uses its argument to build a new monadic result, it can be thought of as an abstract constructor for new actions. In this way return can then be seen as the trivial-action constructor for new actions: the monadic counterpart to the identity function Prelude.id:

id   :: a -> a
id x =  x

IO, in particular

The IO type is special because a Haskell program is one large IO action (usually) called main - for any other IO action to work, it should be part of the chain of I/O actions composed together to form main. Execution of a Haskell program then starts by the Haskell implementation calling main, according to the implementation's private definition of the abstract IO type.

Using monadic actions

Assuming:

  • M is a monadic type;
  • x :: a is some other Haskell value;
  • m :: M a is an action;
  • k :: (a -> M b) is a reaction;
  • M, x, m, k are well-behaved;

then:

(a) r1 = k $ x  :: M b (a new action)
(b) r2 = return x  :: M a (an action whose output value is x)
(c) r3 = m >>= k  :: M b (another new action)
(d) r4 = k =<< m  :: M b (the same as r3)
(e) r5 = k $ m (types don't match)
(f) r6 = k <$> m  :: M (M b) (an action whose output value is also the same as r3)
(g) r7 = join (k <$> m)  :: M b (the same as r3)

where:

infixr 0 $
($)     :: (a -> b) -> a -> b
f $ x   =  f x

infixr 1 =<<
(=<<)   :: (a -> M b) -> M a -> M b
k =<< m =  m >>= k

infixl 4 <$>
(<$>)   :: (a -> b) -> M a -> M b
f <$> m =  m >>= \x -> return (f x)

join    :: M (M a) -> M a
join m  =  m >>= \x -> x

Even though it is a purely-functional language, Haskell can facilitate the use of effect-based computations by presenting them as monadic actions (using a suitable type), with the monadic interface helping to maintain purity. This is the result of the aformentioned separation of concerns, most notably the time of composition. Monadic actions thus resemble programs in a particular EDSL (embedded domain-specific language), "embedded" because they are legitimate Haskell values which describe impure computations - no extraneous annotations are needed. This is how monadic types in Haskell help keep the pure and the impure apart. But not all monadic actions are impure - for them, the benefits of separating concerns are often combined with the automatic formation of a computational "pipeline".

Because they are very useful in practice but rather confronting for beginners, numerous monad tutorials (including this one) exist to help with the learning process.

Supplemental operations for monadic types (non-standard morphisms)

In addition to the two basic ones required by the monadic interface, each monadic type can have its own specific operations e.g. to provide access to and/or enable manipulation of data it implicitly carries which is specific to its nature; cause some specific side-effects; etc.

Running monadic actions

Some monadic types provide an extra operation for running actions, usually if they are purely defined by the type. This allows an action's output value to be obtained directly in Haskell, in contrast to (>>=). While some of the predefined (e.g. from the Prelude) monadic types in Haskell have such an operation (e.g. Prelude.maybe or Prelude.either), most do not.

Why this is expressly not a part of the monadic interface by now should be obvious - impure monadic actions could then be "hidden" inside seemingly-pure definitions, which would end any notion of purity in Haskell. This is why the only general-purpose way to use a monadic action's output value is to direct it to subsequent computations via the reaction which constructs them, through the use of (>>=).