From f07c4bd4dc560330ebe4926c65faa291ade4ffb4 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sat, 3 Oct 2020 15:24:05 +0100 Subject: [PATCH 1/2] Implement `ap` in terms of `Bind` * Moves `ap` to Control.Bind and implements it in terms of just Bind rather than Monad * Moves the Applicative Superclass law from Monad to Bind and renames it to the Apply Superclass law Note that this should be non-breaking since `ap` is now re-exported from Control.Monad. --- src/Control/Bind.purs | 19 ++++++++++++++++++- src/Control/Monad.purs | 21 +-------------------- src/Prelude.purs | 4 ++-- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/Control/Bind.purs b/src/Control/Bind.purs index b0aaaa44..09290937 100644 --- a/src/Control/Bind.purs +++ b/src/Control/Bind.purs @@ -6,6 +6,7 @@ module Control.Bind , composeKleisli, (>=>) , composeKleisliFlipped, (<=<) , ifM + , ap , module Data.Functor , module Control.Apply , module Control.Applicative @@ -32,10 +33,11 @@ import Data.Unit (Unit) -- | -- | where the function argument of `f` is given the name `y`. -- | --- | Instances must satisfy the following law in addition to the `Apply` +-- | Instances must satisfy the following laws in addition to the `Apply` -- | laws: -- | -- | - Associativity: `(x >>= f) >>= g = x >>= (\k -> f k >>= g)` +-- | - Apply Superclass: `apply = ap` -- | -- | Associativity tells us that we can regroup operations which use `do` -- | notation so that we can unambiguously write, for example: @@ -134,3 +136,18 @@ infixr 1 composeKleisliFlipped as <=< -- | ``` ifM :: forall a m. Bind m => m Boolean -> m a -> m a -> m a ifM cond t f = cond >>= \cond' -> if cond' then t else f + + +-- | `ap` provides a default implementation of `(<*>)` for any `Bind`, without +-- | using `(<*>)` as provided by the `Apply`-`Bind` superclass relationship. +-- | +-- | `ap` can therefore be used to write `Apply` instances as follows: +-- | +-- | ```purescript +-- | instance applyF :: Apply F where +-- | apply = ap +-- | ``` +ap :: forall m a b. Bind m => m (a -> b) -> m a -> m b +ap f a = do + f' <- f + map f' a diff --git a/src/Control/Monad.purs b/src/Control/Monad.purs index 03e01bef..7b1d9228 100644 --- a/src/Control/Monad.purs +++ b/src/Control/Monad.purs @@ -1,7 +1,6 @@ module Control.Monad ( class Monad , liftM1 - , ap , whenM , unlessM , module Data.Functor @@ -12,7 +11,7 @@ module Control.Monad import Control.Applicative (class Applicative, liftA1, pure, unless, when) import Control.Apply (class Apply, apply, (*>), (<*), (<*>)) -import Control.Bind (class Bind, bind, ifM, join, (<=<), (=<<), (>=>), (>>=)) +import Control.Bind (class Bind, bind, ap, ifM, join, (<=<), (=<<), (>=>), (>>=)) import Data.Functor (class Functor, map, void, ($>), (<#>), (<$), (<$>)) import Data.Unit (Unit) @@ -27,7 +26,6 @@ import Data.Unit (Unit) -- | -- | - Left Identity: `pure x >>= f = f x` -- | - Right Identity: `x >>= pure = x` --- | - Applicative Superclass: `apply = ap` class (Applicative m, Bind m) <= Monad m instance monadFn :: Monad ((->) r) @@ -50,23 +48,6 @@ liftM1 f a = do a' <- a pure (f a') --- | `ap` provides a default implementation of `(<*>)` for any --- | [`Monad`](#monad), without using `(<*>)` as provided by the --- | [`Apply`](#apply)-[`Monad`](#monad) superclass relationship. --- | --- | `ap` can therefore be used to write [`Apply`](#apply) instances as --- | follows: --- | --- | ```purescript --- | instance applyF :: Apply F where --- | apply = ap --- | ``` -ap :: forall m a b. Monad m => m (a -> b) -> m a -> m b -ap f a = do - f' <- f - a' <- a - pure (f' a') - -- | Perform a monadic action when a condition is true, where the conditional -- | value is also in a monadic context. whenM :: forall m. Monad m => m Boolean -> m Unit -> m Unit diff --git a/src/Prelude.purs b/src/Prelude.purs index 3a1cd439..d280a347 100644 --- a/src/Prelude.purs +++ b/src/Prelude.purs @@ -30,9 +30,9 @@ module Prelude import Control.Applicative (class Applicative, pure, liftA1, unless, when) import Control.Apply (class Apply, apply, (*>), (<*), (<*>)) -import Control.Bind (class Bind, bind, class Discard, discard, ifM, join, (<=<), (=<<), (>=>), (>>=)) +import Control.Bind (class Bind, bind, class Discard, discard, ifM, ap, join, (<=<), (=<<), (>=>), (>>=)) import Control.Category (class Category, identity) -import Control.Monad (class Monad, ap, liftM1, unlessM, whenM) +import Control.Monad (class Monad, liftM1, unlessM, whenM) import Control.Semigroupoid (class Semigroupoid, compose, (<<<), (>>>)) import Data.Boolean (otherwise) From c5bbdfbd61f685be5ee8d2f37a12618dbf823798 Mon Sep 17 00:00:00 2001 From: Harry Garrood Date: Sat, 3 Oct 2020 19:22:33 +0100 Subject: [PATCH 2/2] Update src/Control/Bind.purs Co-authored-by: Thomas Honeyman --- src/Control/Bind.purs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Control/Bind.purs b/src/Control/Bind.purs index 09290937..6d7c145d 100644 --- a/src/Control/Bind.purs +++ b/src/Control/Bind.purs @@ -137,7 +137,6 @@ infixr 1 composeKleisliFlipped as <=< ifM :: forall a m. Bind m => m Boolean -> m a -> m a -> m a ifM cond t f = cond >>= \cond' -> if cond' then t else f - -- | `ap` provides a default implementation of `(<*>)` for any `Bind`, without -- | using `(<*>)` as provided by the `Apply`-`Bind` superclass relationship. -- |