Skip to content
Open
Changes from all 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
34 changes: 16 additions & 18 deletions src/Control/Selective.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
-----------------------------------------------------------------------------
module Control.Selective (
-- * Type class
Selective (..), (<*?), branch, selectA, apS, selectM,
Selective (..), (<*?), selectA, apS, selectM,

-- * Conditional combinators
ifS, whenS, fromMaybeS, orElse, andAlso, untilRight, whileS, (<||>), (<&&>),
Expand Down Expand Up @@ -47,9 +47,12 @@ import Data.Functor.Identity
import Data.Functor.Product
import Data.List.NonEmpty
import Data.Proxy
import Data.Semigroup (Semigroup (..))
import GHC.Conc (STM)

#if !MIN_VERSION_base(4,11,0)
import Data.Semigroup (Semigroup (..))
#endif

import qualified Control.Monad.Trans.RWS.Strict as S
import qualified Control.Monad.Trans.State.Strict as S
import qualified Control.Monad.Trans.Writer.Strict as S
Expand Down Expand Up @@ -136,8 +139,19 @@ import qualified Control.Monad.Trans.Writer.Strict as S
--
-- If f is also a 'Monad', we require that 'select' = 'selectM', from which one
-- can prove '<*>' @=@ 'apS'.
--
-- The 'branch' method is a natural generalisation of 'select': instead of
-- skipping an unnecessary effect, it chooses which of the two given effectful
-- functions to apply to a given argument; the other effect is unnecessary. It
-- is possible to implement 'branch' in terms of 'select', which is a good
-- puzzle (give it a try!), and 'select' via 'branch' (which is much easier).
class Applicative f => Selective f where
{-# MINIMAL select | branch #-}
select :: f (Either a b) -> f (a -> b) -> f b
select x y = branch x y (pure id)

branch :: f (Either a b) -> f (a -> c) -> f (b -> c) -> f c
branch x l r = fmap (fmap Left) x <*? fmap (fmap Right) l <*? r

-- | An operator alias for 'select', which is sometimes convenient. It tries to
-- follow the notational convention for 'Applicative' operators. The angle
Expand All @@ -148,22 +162,6 @@ class Applicative f => Selective f where

infixl 4 <*?

-- | The 'branch' function is a natural generalisation of 'select': instead of
-- skipping an unnecessary effect, it chooses which of the two given effectful
-- functions to apply to a given argument; the other effect is unnecessary. It
-- is possible to implement 'branch' in terms of 'select', which is a good
-- puzzle (give it a try!).
--
-- We can also implement 'select' via 'branch':
--
-- @
-- selectB :: Selective f => f (Either a b) -> f (a -> b) -> f b
-- selectB x y = branch x y (pure id)
-- @
--
branch :: Selective f => f (Either a b) -> f (a -> c) -> f (b -> c) -> f c
branch x l r = fmap (fmap Left) x <*? fmap (fmap Right) l <*? r

-- | We can write a function with the type signature of 'select' using the
-- 'Applicative' type class, but it will always execute the effects associated
-- with the second argument, hence being potentially less efficient.
Expand Down