Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement initial BindLeft and MonadLeft #17

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
b9075b6
Implement initial BindLeft and MonadLeft
JordanMartinez Nov 15, 2020
0acc6fc
Implement left version of Apply
JordanMartinez Nov 26, 2020
610efd5
Implement left version of Applicative
JordanMartinez Nov 26, 2020
6baa15b
Update definitino of MonadLeft to depend on ApplicativeLeft
JordanMartinez Nov 26, 2020
8bada1b
Add left versions of whenM and unlessM
JordanMartinez Nov 26, 2020
104768c
Implement qualified do for MonadLeft
JordanMartinez Nov 26, 2020
5f4da8a
Change l prefix to Left suffix
JordanMartinez Dec 18, 2020
a193890
Abbreviate ldiscard implementation
JordanMartinez Dec 18, 2020
3a0ed53
Update implementation of compoesKleisliLeft to match right counterpart
JordanMartinez Dec 18, 2020
948d34e
Change w to m
JordanMartinez Dec 18, 2020
99b4266
Fix compiler error
JordanMartinez Dec 18, 2020
4793eec
Fix references to lwhen and lunless to their new names
JordanMartinez Dec 18, 2020
32a32a1
Remove unused import
JordanMartinez Dec 18, 2020
92dd8ca
Merge branch 'master' into addBindLeft
JordanMartinez Aug 23, 2021
f80b235
Move files into Control.Bifunctor module
JordanMartinez Aug 24, 2021
4484988
Update module names to match files
JordanMartinez Aug 24, 2021
1c5b583
Add pure export to Qualified module
JordanMartinez Aug 24, 2021
5f23d85
Update docs to use `BiLeft` convention
JordanMartinez Aug 24, 2021
5efcadf
Add kind sig to BindLeft
JordanMartinez Aug 24, 2021
3354b83
Add kind sig to other type classes
JordanMartinez Aug 24, 2021
f188683
Fix remaining import issues
JordanMartinez Aug 24, 2021
a3e9546
Change type paramter to m
JordanMartinez Aug 24, 2021
6bc197a
Move files out of Bifunctor folder
JordanMartinez Aug 25, 2021
a64efb1
Drop Bifunctor module prefix on MonadLeft classes
JordanMartinez Aug 25, 2021
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
18 changes: 18 additions & 0 deletions src/Control/ApplicativeLeft.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Control.ApplicativeLeft where

import Prelude
import Control.ApplyLeft (class ApplyLeft, lapply)

-- | Same as `Applicative` but works on the left parameter in a Bifunctor.
class ApplyLeft w <= ApplicativeLeft w where
JordanMartinez marked this conversation as resolved.
Show resolved Hide resolved
lpure :: forall a b. a -> w a b

-- | Perform an applicative action when a condition is true.
lwhen :: forall m c. ApplicativeLeft m => Boolean -> m Unit c -> m Unit c
lwhen true m = m
lwhen false _ = lpure unit

-- | Perform an applicative action unless a condition is true.
lunless :: forall m c. ApplicativeLeft m => Boolean -> m Unit c -> m Unit c
lunless false m = m
lunless true _ = lpure unit
36 changes: 36 additions & 0 deletions src/Control/ApplyLeft.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Control.ApplyLeft where

import Prelude
import Data.Bifunctor (class Bifunctor, lmap)

-- | Same as `Apply` but works on the left parameter in a Bifunctor.
class Bifunctor w <= ApplyLeft w where
JordanMartinez marked this conversation as resolved.
Show resolved Hide resolved
lapply :: forall a b c. w (a -> b) c -> w a c -> w b c

-- | Combine two effectful actions, keeping only the result of the first.
lapplyFirst :: forall a b c w. ApplyLeft w => w a c -> w b c -> w a c
lapplyFirst a b = lapply (lmap const a) b

-- | Combine two effectful actions, keeping only the result of the second.
lapplySecond :: forall w a b c. ApplyLeft w => w a c -> w b c -> w b c
lapplySecond a b = lapply (lmap (const identity) a) b

-- | Lift a function of two arguments to a function which accepts and returns
-- | values wrapped with the type constructor `f`.
llift2 :: forall a b c w x. ApplyLeft w => (a -> b -> c) -> w a x -> w b x -> w c x
llift2 f a b = f `lmap` a `lapply` b

-- | Lift a function of three arguments to a function which accepts and returns
-- | values wrapped with the type constructor `f`.
llift3 :: forall a b c d w x. ApplyLeft w => (a -> b -> c -> d) -> w a x -> w b x -> w c x -> w d x
llift3 f a b c = f `lmap` a `lapply` b `lapply` c

-- | Lift a function of four arguments to a function which accepts and returns
-- | values wrapped with the type constructor `f`.
llift4 :: forall a b c d e w x. ApplyLeft w => (a -> b -> c -> d -> e) -> w a x -> w b x -> w c x -> w d x -> w e x
llift4 f a b c d = f `lmap` a `lapply` b `lapply` c `lapply` d

-- | Lift a function of five arguments to a function which accepts and returns
-- | values wrapped with the type constructor `f`.
llift5 :: forall a b c d e g w x. ApplyLeft w => (a -> b -> c -> d -> e -> g) -> w a x -> w b x -> w c x -> w d x -> w e x -> w g x
llift5 f a b c d e = f `lmap` a `lapply` b `lapply` c `lapply` d `lapply` e
30 changes: 30 additions & 0 deletions src/Control/BindLeft.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Control.BindLeft where

import Data.Unit (Unit)
import Control.Category (identity)

-- | Same as `Discard` but works on the left parameter of the bifunctor
-- | rather than the right parameter, which is the default for `Discard`.
class DiscardLeft a where
ldiscard :: forall k (m :: Type -> k -> Type) b r. BindLeft m => m a r -> (a -> m b r) -> m b r

instance discardLeftUnit :: DiscardLeft Unit where
ldiscard ma aToMB = lbind ma aToMB
JordanMartinez marked this conversation as resolved.
Show resolved Hide resolved

-- | Same as `Bind` but works on the left parameter of the bifunctor
-- | rather than the right parameter, which is the default for `Bind`.
class BindLeft :: forall k. (Type -> k -> Type) -> Constraint
class BindLeft m where
lbind :: forall a b r. m a r -> (a -> m b r) -> m b r

ljoin :: forall m a r. BindLeft m => m (m a r) r -> m a r
ljoin m = lbind m identity

lcomposeKleisli :: forall m a b c r. BindLeft m => (a -> m b r) -> (b -> m c r) -> a -> m c r
lcomposeKleisli aToMB bToMC a = lbind (aToMB a) bToMC

lcomposeKleisliFlipped :: forall m a b c r. BindLeft m => (b -> m c r) -> (a -> m b r) -> a -> m c r
lcomposeKleisliFlipped bToMC aToMB a = lbind (aToMB a) bToMC
JordanMartinez marked this conversation as resolved.
Show resolved Hide resolved

lifM :: forall m a r. BindLeft m => m Boolean r -> m a r -> m a r -> m a r
lifM cond truePath falsePath = lbind cond \b -> if b then truePath else falsePath
JordanMartinez marked this conversation as resolved.
Show resolved Hide resolved
17 changes: 17 additions & 0 deletions src/Control/MonadLeft.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Control.MonadLeft where

import Control.ApplicativeLeft (class ApplicativeLeft, lwhen, lunless)
import Control.BindLeft (class BindLeft, lbind)
import Data.Unit (Unit)

class (ApplicativeLeft m, BindLeft m) <= MonadLeft m

-- | Perform a monadic action when a condition is true, where the conditional
-- | value is also in a monadic context.
lwhenM :: forall m c. MonadLeft m => m Boolean c -> m Unit c -> m Unit c
lwhenM mb m = lbind mb \b -> lwhen b m

-- | Perform a monadic action unless a condition is true, where the conditional
-- | value is also in a monadic context.
lunlessM :: forall m c. MonadLeft m => m Boolean c -> m Unit c -> m Unit c
lunlessM mb m = lbind mb \b -> lunless b m
40 changes: 40 additions & 0 deletions src/Control/MonadLeft/Qualified.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
-- | Enables one to use `ado notation` and `do notation` on the left parameter
-- | of a Bifunctor.
-- |
-- | `ado notation` example
-- | ```
-- | import Control.MonadLeft.Qualified as MonadLeft
-- |
-- | foo :: Either Int String -> Either String String
-- | foo comp = MonadLeft.ado
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should the convention be Flip.do rather than MonadLeft.do?

Copy link
Member

Choose a reason for hiding this comment

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

MonadLeft.do seems more meaningful to me than Flip.do, but writing MonadLeft.ado for a type with only an ApplicativeLeft instance and no MonadLeft is odd.

Copy link
Contributor Author

@JordanMartinez JordanMartinez Dec 18, 2020

Choose a reason for hiding this comment

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

I read MonadLeft.do / MonadLeft.ado less of a "hey, this thing is a monad!" and more of a "hey, we're using the MonadLeft type class hierarchy here." That's how I read Ix.do, React.do, and Hooks.do.

-- | a <- comp
-- | b <- comp
-- | in show $ a + b
-- | ```
-- | `do notation` example
-- | ```
-- | import Control.MonadLeft.Qualified as MonadLeft
-- |
-- | foo :: Either Int String -> Either String String
-- | foo comp = MonadLeft.do
-- | a <- comp
-- | b <- comp
-- | lpure $ show $ a + b
-- | ```
module Control.MonadLeft.Qualified where
JordanMartinez marked this conversation as resolved.
Show resolved Hide resolved

import Control.ApplyLeft (class ApplyLeft, lapply)
import Control.BindLeft (class BindLeft, lbind, class DiscardLeft, ldiscard)
import Data.Bifunctor (class Bifunctor, lmap)

discard :: forall m a b r. DiscardLeft a => BindLeft m => m a r -> (a -> m b r) -> m b r
discard = ldiscard

bind :: forall m a b r. BindLeft m => m a r -> (a -> m b r) -> m b r
bind = lbind

apply :: forall m a b c. ApplyLeft m => m (a -> b) c -> m a c -> m b c
apply = lapply

map :: forall m a b c. Bifunctor m => (a -> b) -> m a c -> m b c
map = lmap