Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Breaking changes:

New features:

- Add lazily evaluated versions of the conditional functions for applicatives
and monads ifM', when', whenM', unless' and unlessM' (#313)

Bugfixes:

Other improvements:
Expand Down
16 changes: 14 additions & 2 deletions src/Control/Applicative.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ module Control.Applicative
, pure
, liftA1
, unless
, unless'
, when
, when'
, module Control.Apply
, module Data.Functor
) where

import Control.Apply (class Apply, apply, (*>), (<*), (<*>))
import Control.Category ((<<<))

import Data.Boolean (otherwise)
import Data.Functor (class Functor, map, void, ($>), (<#>), (<$), (<$>))
import Data.HeytingAlgebra (not)
import Data.Unit (Unit, unit)
import Type.Proxy (Proxy(..))

Expand Down Expand Up @@ -64,7 +69,14 @@ when :: forall m. Applicative m => Boolean -> m Unit -> m Unit
when true m = m
when false _ = pure unit

-- | Perform an applicative action lazily when a condition is true.
when' :: forall m a. Applicative m => (a -> Boolean) -> (a -> m Unit) -> a -> m Unit
when' f m a = if f a then m a else pure unit

-- | Perform an applicative action unless a condition is true.
unless :: forall m. Applicative m => Boolean -> m Unit -> m Unit
unless false m = m
unless true _ = pure unit
unless = when <<< not

-- | Perform an applicative action lazily unless a condition is true.
unless' :: forall m a. Applicative m => (a -> Boolean) -> (a -> m Unit) -> a -> m Unit
unless' = when' <<< not
20 changes: 20 additions & 0 deletions src/Control/Bind.purs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module Control.Bind
, composeKleisliFlipped
, (<=<)
, ifM
, ifM'
, module Data.Functor
, module Control.Apply
, module Control.Applicative
Expand Down Expand Up @@ -148,3 +149,22 @@ 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

-- | Similar to `ifM` but for use in cases where one of the monadic actions may
-- | be expensive to compute. As PureScript is not lazy, the standard `ifM` has
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps "construct" rather than "compute" in this first sentence? And really I think that would suffice for the explanation, elaborating further on strict eval semantics only really applies to people who are newly coming to PS from Haskell. 😉

Having the code example is still good though 👍

-- | to construct both monadic actions before returning the result, whereas here
-- | only the corresponding monadic action is constructed.
-- |
-- | ```purescript
-- | main :: Effect Unit
-- | main = do
-- | response <- ifM' exists update create user
-- | log response
-- |
-- | where
-- | create :: User -> Effect String
-- | update :: User -> Effect String
-- | exists :: User -> Effect Boolean
-- | ```
ifM' :: forall a b m. Bind m => (a -> m Boolean) -> (a -> m b) -> (a -> m b) -> a -> m b
ifM' cond t f a = cond a >>= \cond' -> if cond' then t a else f a
27 changes: 19 additions & 8 deletions src/Control/Monad.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ module Control.Monad
( class Monad
, liftM1
, whenM
, whenM'
, unlessM
, unlessM'
, ap
, module Data.Functor
, module Control.Apply
Expand All @@ -12,10 +14,13 @@ 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, join, (<=<), (=<<), (>=>), (>>=), ifM, ifM')
import Control.Category ((>>>))

import Data.HeytingAlgebra (not)
import Data.Function (($))
import Data.Functor (class Functor, map, void, ($>), (<#>), (<$), (<$>))
import Data.Unit (Unit)
import Data.Unit (Unit, unit)
import Type.Proxy (Proxy)

-- | The `Monad` type class combines the operations of the `Bind` and
Expand Down Expand Up @@ -55,16 +60,22 @@ liftM1 f a = do
-- | 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
whenM mb m = do
b <- mb
when b m
whenM mb m = ifM mb m $ pure unit

-- | Perform a monadic action lazily when a condition is true, where the conditional
-- | value is also in a monadic context.
whenM' :: forall m a. Monad m => (a -> m Boolean) -> (a -> m Unit) -> a -> m Unit
whenM' mb m = ifM' mb m $ \_ -> pure unit

-- | Perform a monadic action unless a condition is true, where the conditional
-- | value is also in a monadic context.
unlessM :: forall m. Monad m => m Boolean -> m Unit -> m Unit
unlessM mb m = do
b <- mb
unless b m
unlessM mb = whenM $ not <$> mb

-- | Perform a monadic action lazily unless a condition is true, where the conditional
-- | value is also in a monadic context.
unlessM' :: forall m a. Monad m => (a -> m Boolean) -> (a -> m Unit) -> a -> m Unit
unlessM' mb = whenM' $ \x -> mb x >>= not >>> pure

-- | `ap` provides a default implementation of `(<*>)` for any `Monad`, without
-- | using `(<*>)` as provided by the `Apply`-`Monad` superclass relationship.
Expand Down
6 changes: 3 additions & 3 deletions src/Prelude.purs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ module Prelude
, module Data.Void
) where

import Control.Applicative (class Applicative, pure, liftA1, unless, when)
import Control.Applicative (class Applicative, liftA1, pure, unless, unless', when, 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, ifM', join, (<=<), (=<<), (>=>), (>>=))
import Control.Category (class Category, identity)
import Control.Monad (class Monad, liftM1, unlessM, whenM, ap)
import Control.Monad (class Monad, ap, liftM1, unlessM, unlessM', whenM, whenM')
import Control.Semigroupoid (class Semigroupoid, compose, (<<<), (>>>))

import Data.Boolean (otherwise)
Expand Down
Loading