From d8fe7d6b20775ee2b4a6452abcf21bdc04d4e892 Mon Sep 17 00:00:00 2001 From: rightfold Date: Wed, 29 Mar 2017 16:14:15 +0200 Subject: [PATCH 1/8] Update dependencies for psc 0.10 and add IOSync --- bower.json | 12 ++- src/Control/Monad/IO.purs | 113 +++++++++++----------------- src/Control/Monad/IO/Class.purs | 14 ++-- src/Control/Monad/IOSync.purs | 58 ++++++++++++++ src/Control/Monad/IOSync/Class.purs | 16 ++++ 5 files changed, 133 insertions(+), 80 deletions(-) create mode 100644 src/Control/Monad/IOSync.purs create mode 100644 src/Control/Monad/IOSync/Class.purs diff --git a/bower.json b/bower.json index 5006e68..7ae5ef6 100644 --- a/bower.json +++ b/bower.json @@ -7,9 +7,17 @@ "output" ], "dependencies": { - "purescript-aff": "1.1.0" + "purescript-aff": "^2.0.3", + "purescript-control": "^2.0.0", + "purescript-eff": "^2.0.0", + "purescript-exceptions": "^2.0.0", + "purescript-monoid": "^2.2.0", + "purescript-newtype": "^1.3.0", + "purescript-prelude": "^2.5.0", + "purescript-tailrec": "^2.0.2", + "purescript-transformers": "^2.3.0" }, "devDependencies": { - "purescript-psci-support": "^1.0.0" + "purescript-psci-support": "^2.0.0" } } diff --git a/src/Control/Monad/IO.purs b/src/Control/Monad/IO.purs index 3a619ed..7578061 100644 --- a/src/Control/Monad/IO.purs +++ b/src/Control/Monad/IO.purs @@ -1,87 +1,58 @@ -module Control.Monad.IO (IO, INFINITY, AffIO(..), runIO) where - import Prelude +module Control.Monad.IO + ( INFINITY + , IO(..) + ) where - import Control.Alt (class Alt, alt) - import Control.Alternative (class Alternative) - import Control.Monad.Eff (Eff) - import Control.Monad.Eff.Class (class MonadEff, liftEff) - import Control.Monad.Aff (Aff) - import Control.Monad.Aff.Class (class MonadAff) - import Control.Monad.Eff.Exception (Error) - import Control.Monad.Error.Class (class MonadError, throwError, catchError) - import Control.Monad.Rec.Class (class MonadRec, tailRecM) - import Control.MonadPlus (class MonadZero, class MonadPlus, empty) - import Control.Parallel.Class (class MonadRace, class MonadPar, par, race, stall) - import Control.Plus (class Plus) +import Control.Alt (class Alt) +import Control.Alternative (class Alternative) +import Control.Monad.Aff (Aff) +import Control.Monad.Aff.Class (class MonadAff) +import Control.Monad.Aff.Unsafe (unsafeCoerceAff) +import Control.Monad.Eff.Class (class MonadEff, liftEff) +import Control.Monad.Eff.Exception (Error) +import Control.Monad.Eff.Unsafe (unsafeCoerceEff) +import Control.Monad.Error.Class (class MonadError) +import Control.Monad.IO (INFINITY) +import Control.Monad.Rec.Class (class MonadRec) +import Control.MonadPlus (class MonadPlus) +import Control.MonadZero (class MonadZero) +import Control.Plus (class Plus) +import Data.Monoid (class Monoid) +import Data.Newtype (class Newtype, wrap) +import Prelude - import Data.Monoid (class Monoid, mempty) +foreign import data INFINITY :: Effect - import Unsafe.Coerce (unsafeCoerce) +newtype IO a = IO (Aff (infinity :: INFINITY) a) - foreign import data IO :: * -> * +derive instance newtypeIO :: Newtype (IO a) _ - foreign import data INFINITY :: ! +derive newtype instance functorIO :: Functor IO +derive newtype instance applyIO :: Apply IO +derive newtype instance applicativeIO :: Applicative IO +derive newtype instance bindIO :: Bind IO +derive newtype instance monadIO :: Monad IO - type AffIO a = Aff (infinity :: INFINITY) a +derive newtype instance monadRecIO :: MonadRec IO - runIO :: forall a. IO a -> AffIO a - runIO = unsafeCoerce +derive newtype instance semigroupIO :: (Semigroup a) => Semigroup (IO a) - toIO :: forall e a. Aff e a -> IO a - toIO = unsafeCoerce +derive newtype instance monoidIO :: (Monoid a) => Monoid (IO a) - instance semigroupIO :: (Semigroup a) => Semigroup (IO a) where - append a b = toIO (append (runIO a) (runIO b)) +instance monadAffIO :: MonadAff eff IO where + liftAff = wrap <<< unsafeCoerceAff - instance monoidIO :: (Monoid a) => Monoid (IO a) where - mempty = toIO (pure mempty) +instance monadEffIO :: MonadEff eff IO where + liftEff = wrap <<< liftEff <<< unsafeCoerceEff - instance functorIO :: Functor IO where - map f fa = toIO (map f (runIO fa)) +derive newtype instance monadErrorIO :: MonadError Error IO - instance applyIO :: Apply IO where - apply ff fa = toIO (apply (runIO ff) (runIO fa)) +derive newtype instance altIO :: Alt IO - instance applicativeIO :: Applicative IO where - pure v = toIO (pure v) +derive newtype instance plusIO :: Plus IO - instance bindIO :: Bind IO where - bind fa f = toIO (bind (runIO fa) (unsafeCoerce f)) +derive newtype instance alternativeIO :: Alternative IO - instance monadIO :: Monad IO +derive newtype instance monadZeroIO :: MonadZero IO - instance monadEffIO :: MonadEff e IO where - liftEff = liftEff' - where - liftEff' :: forall a. Eff e a -> IO a - liftEff' eff = toIO (liftEff eff :: Aff e a) - - instance monadAffIO :: MonadAff e IO where - liftAff = toIO - - instance monadErrorIO :: MonadError Error IO where - throwError e = toIO (throwError e) - - catchError io f = toIO (catchError (runIO io) (runIO <$> f)) - - instance altIO :: Alt IO where - alt a1 a2 = toIO (alt (runIO a1) (runIO a2)) - - instance plusIO :: Plus IO where - empty = toIO empty - - instance alternativeIO :: Alternative IO - - instance monadZero :: MonadZero IO - - instance monadPlusIO :: MonadPlus IO - - instance monadRecIO :: MonadRec IO where - tailRecM f a = toIO (tailRecM (unsafeCoerce f) a) - - instance monadParIO :: MonadPar IO where - par f ma mb = toIO (par f (runIO ma) (runIO mb)) - - instance monadRaceIO :: MonadRace IO where - stall = toIO stall - race a1 a2 = toIO (race (runIO a1) (runIO a2)) +derive newtype instance monadPlusIO :: MonadPlus IO diff --git a/src/Control/Monad/IO/Class.purs b/src/Control/Monad/IO/Class.purs index 0156fe1..fe6709c 100644 --- a/src/Control/Monad/IO/Class.purs +++ b/src/Control/Monad/IO/Class.purs @@ -1,10 +1,10 @@ module Control.Monad.IO.Class where - import Control.Category (id) - import Control.Monad (class Monad) - import Control.Monad.IO (IO) - class Monad m <= MonadIO m where - liftIO :: forall a. IO a -> m a +import Control.Monad.IO (IO) +import Prelude - instance monadIOIO :: MonadIO IO where - liftIO = id +class (Monad m) <= MonadIO m where + liftIO :: ∀ a. IO a -> m a + +instance monadIOIO :: MonadIO IO where + liftIO = id diff --git a/src/Control/Monad/IOSync.purs b/src/Control/Monad/IOSync.purs new file mode 100644 index 0000000..db921ab --- /dev/null +++ b/src/Control/Monad/IOSync.purs @@ -0,0 +1,58 @@ +module Control.Monad.IOSync + ( module Control.Monad.IO + , IOSync(..) + ) where + +import Control.Alt (class Alt) +import Control.Alternative (class Alternative) +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Class (class MonadEff, liftEff) +import Control.Monad.Eff.Exception (Error, catchException, error, throwException) +import Control.Monad.Eff.Unsafe (unsafeCoerceEff) +import Control.Monad.Error.Class (class MonadError, catchError, throwError) +import Control.Monad.IO (INFINITY) +import Control.Monad.Rec.Class (class MonadRec) +import Control.MonadPlus (class MonadPlus) +import Control.MonadZero (class MonadZero) +import Control.Plus (class Plus) +import Data.Monoid (class Monoid, mempty) +import Data.Newtype (class Newtype, unwrap, wrap) +import Prelude + +newtype IOSync a = IOSync (Eff (infinity :: INFINITY) a) + +derive instance newtypeIOSync :: Newtype (IOSync a) _ + +derive newtype instance functorIOSync :: Functor IOSync +derive newtype instance applyIOSync :: Apply IOSync +derive newtype instance applicativeIOSync :: Applicative IOSync +derive newtype instance bindIOSync :: Bind IOSync +derive newtype instance monadIOSync :: Monad IOSync + +derive newtype instance monadRecIOSync :: MonadRec IOSync + +instance semigroupIOSync :: (Semigroup a) => Semigroup (IOSync a) where + append a b = append <$> a <*> b + +instance monoidIOSync :: (Monoid a) => Monoid (IOSync a) where + mempty = pure mempty + +instance monadEffIOSync :: MonadEff eff IOSync where + liftEff = wrap <<< unsafeCoerceEff + +instance monadErrorIOSync :: MonadError Error IOSync where + catchError a k = liftEff $ + catchException (\e -> unwrap $ k e) (unsafeCoerceEff $ unwrap a) + throwError = liftEff <<< throwException + +instance altIOSync :: Alt IOSync where + alt a b = a `catchError` const b + +instance plusIOSync :: Plus IOSync where + empty = throwError $ error "plusIOSync.empty" + +instance alternativeIOSync :: Alternative IOSync + +instance monadZeroIOSync :: MonadZero IOSync + +instance monadPlusIOSync :: MonadPlus IOSync diff --git a/src/Control/Monad/IOSync/Class.purs b/src/Control/Monad/IOSync/Class.purs new file mode 100644 index 0000000..b7a2a22 --- /dev/null +++ b/src/Control/Monad/IOSync/Class.purs @@ -0,0 +1,16 @@ +module Control.Monad.IOSync.Class where + +import Control.Monad.Eff.Class (liftEff) +import Control.Monad.IO (IO) +import Control.Monad.IOSync (IOSync) +import Data.Newtype (unwrap, wrap) +import Prelude + +class (Monad m) <= MonadIOSync m where + liftIOSync :: ∀ a. IOSync a -> m a + +instance monadIOSyncIOSync :: MonadIOSync IOSync where + liftIOSync = id + +instance monadIOSyncIO :: MonadIOSync IO where + liftIOSync = wrap <<< liftEff <<< unwrap From d3f1a4c2bfce34357421a18a8749782b87f07260 Mon Sep 17 00:00:00 2001 From: rightfold Date: Wed, 29 Mar 2017 16:15:35 +0200 Subject: [PATCH 2/8] Update readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9e78d5f..5e9f5fd 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Similarly, if we were using `Free` directly, instead of using type classes to ab ```haskell data ConfigF a = ReadConfig (Config -> a) - + serverAddress :: ReaderT (PrismT' f ConfigF) (Free f) InetAddress ``` @@ -58,18 +58,20 @@ Therefore, MTL and direct-Free approaches can be considered alternatives to Pure `IO` only has one function, which should only be used in your `main`: ```haskell -runIO :: forall a. IO a -> Aff (infinity :: INFINITY) a +unwrap :: forall a. IO a -> Aff (infinity :: INFINITY) a ``` This converts an `IO` into an `Aff`, which you can then "convert" into a runnable `Eff` using `launchAff` or `runAff`. The effect row is closed, which is intentional because `INFINITY` represents -all possible effects. This will help ensure you only call `runIO` at the top +all possible effects. This will help ensure you only call `unwrap` at the top level of your program. Besides this, `IO` has almost all the same instances as `Aff`, and may be used in the same way. In addition, a new `MonadIO` class has been introduced which allows you to lift `IO` computations into other monads that are as powerful. +Similarly, `IOSync` exists as an alternative for `Eff`. + Happy nuke launching! From cd31d7808527acfba16da6af7a57459a4a3e7ee7 Mon Sep 17 00:00:00 2001 From: rightfold Date: Wed, 29 Mar 2017 17:45:07 +0200 Subject: [PATCH 3/8] Use type (~>) --- src/Control/Monad/IO/Class.purs | 2 +- src/Control/Monad/IOSync/Class.purs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Control/Monad/IO/Class.purs b/src/Control/Monad/IO/Class.purs index fe6709c..9d636d1 100644 --- a/src/Control/Monad/IO/Class.purs +++ b/src/Control/Monad/IO/Class.purs @@ -4,7 +4,7 @@ import Control.Monad.IO (IO) import Prelude class (Monad m) <= MonadIO m where - liftIO :: ∀ a. IO a -> m a + liftIO :: IO ~> m instance monadIOIO :: MonadIO IO where liftIO = id diff --git a/src/Control/Monad/IOSync/Class.purs b/src/Control/Monad/IOSync/Class.purs index b7a2a22..c55bf6a 100644 --- a/src/Control/Monad/IOSync/Class.purs +++ b/src/Control/Monad/IOSync/Class.purs @@ -7,7 +7,7 @@ import Data.Newtype (unwrap, wrap) import Prelude class (Monad m) <= MonadIOSync m where - liftIOSync :: ∀ a. IOSync a -> m a + liftIOSync :: IOSync ~> m instance monadIOSyncIOSync :: MonadIOSync IOSync where liftIOSync = id From 7bcde1321909b411888acacd7625a42a83164a0d Mon Sep 17 00:00:00 2001 From: rightfold Date: Thu, 30 Mar 2017 11:13:57 +0200 Subject: [PATCH 4/8] Elaborate on unwrap --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e9f5fd..067d7d1 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,11 @@ Therefore, MTL and direct-Free approaches can be considered alternatives to Pure # Usage -`IO` only has one function, which should only be used in your `main`: +`IO` is a newtype for `Aff`, which you can unwrap to be used in your `main`: ```haskell +import Data.Newtype (unwrap) + unwrap :: forall a. IO a -> Aff (infinity :: INFINITY) a ``` From 5fb99b03d2f03218064e13f5f145ce10644d9bee Mon Sep 17 00:00:00 2001 From: rightfold Date: Thu, 30 Mar 2017 11:17:35 +0200 Subject: [PATCH 5/8] Delete MonadPlus instances Fixes #3. --- src/Control/Monad/IO.purs | 3 --- src/Control/Monad/IOSync.purs | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/Control/Monad/IO.purs b/src/Control/Monad/IO.purs index 7578061..faaae81 100644 --- a/src/Control/Monad/IO.purs +++ b/src/Control/Monad/IO.purs @@ -14,7 +14,6 @@ import Control.Monad.Eff.Unsafe (unsafeCoerceEff) import Control.Monad.Error.Class (class MonadError) import Control.Monad.IO (INFINITY) import Control.Monad.Rec.Class (class MonadRec) -import Control.MonadPlus (class MonadPlus) import Control.MonadZero (class MonadZero) import Control.Plus (class Plus) import Data.Monoid (class Monoid) @@ -54,5 +53,3 @@ derive newtype instance plusIO :: Plus IO derive newtype instance alternativeIO :: Alternative IO derive newtype instance monadZeroIO :: MonadZero IO - -derive newtype instance monadPlusIO :: MonadPlus IO diff --git a/src/Control/Monad/IOSync.purs b/src/Control/Monad/IOSync.purs index db921ab..980047b 100644 --- a/src/Control/Monad/IOSync.purs +++ b/src/Control/Monad/IOSync.purs @@ -12,7 +12,6 @@ import Control.Monad.Eff.Unsafe (unsafeCoerceEff) import Control.Monad.Error.Class (class MonadError, catchError, throwError) import Control.Monad.IO (INFINITY) import Control.Monad.Rec.Class (class MonadRec) -import Control.MonadPlus (class MonadPlus) import Control.MonadZero (class MonadZero) import Control.Plus (class Plus) import Data.Monoid (class Monoid, mempty) @@ -54,5 +53,3 @@ instance plusIOSync :: Plus IOSync where instance alternativeIOSync :: Alternative IOSync instance monadZeroIOSync :: MonadZero IOSync - -instance monadPlusIOSync :: MonadPlus IOSync From 259046c00bb6d35c4c2759d2afc1e1978ad02b5a Mon Sep 17 00:00:00 2001 From: rightfold Date: Thu, 30 Mar 2017 11:23:04 +0200 Subject: [PATCH 6/8] Add launchIO --- src/Control/Monad/IO.purs | 15 +++++++++------ src/Control/Monad/IO/Effect.purs | 5 +++++ src/Control/Monad/IOSync.purs | 4 ++-- 3 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 src/Control/Monad/IO/Effect.purs diff --git a/src/Control/Monad/IO.purs b/src/Control/Monad/IO.purs index faaae81..dbd43c2 100644 --- a/src/Control/Monad/IO.purs +++ b/src/Control/Monad/IO.purs @@ -1,29 +1,32 @@ module Control.Monad.IO - ( INFINITY + ( module Control.Monad.IO.Effect , IO(..) + , launchIO ) where import Control.Alt (class Alt) import Control.Alternative (class Alternative) -import Control.Monad.Aff (Aff) +import Control.Monad.Aff (Aff, launchAff) import Control.Monad.Aff.Class (class MonadAff) import Control.Monad.Aff.Unsafe (unsafeCoerceAff) import Control.Monad.Eff.Class (class MonadEff, liftEff) import Control.Monad.Eff.Exception (Error) import Control.Monad.Eff.Unsafe (unsafeCoerceEff) import Control.Monad.Error.Class (class MonadError) -import Control.Monad.IO (INFINITY) +import Control.Monad.IO.Effect (INFINITY) +import Control.Monad.IOSync (IOSync) import Control.Monad.Rec.Class (class MonadRec) import Control.MonadZero (class MonadZero) import Control.Plus (class Plus) import Data.Monoid (class Monoid) -import Data.Newtype (class Newtype, wrap) +import Data.Newtype (class Newtype, unwrap, wrap) import Prelude -foreign import data INFINITY :: Effect - newtype IO a = IO (Aff (infinity :: INFINITY) a) +launchIO :: ∀ a. IO a -> IOSync Unit +launchIO = void <<< liftEff <<< launchAff <<< unwrap + derive instance newtypeIO :: Newtype (IO a) _ derive newtype instance functorIO :: Functor IO diff --git a/src/Control/Monad/IO/Effect.purs b/src/Control/Monad/IO/Effect.purs new file mode 100644 index 0000000..67fe6c9 --- /dev/null +++ b/src/Control/Monad/IO/Effect.purs @@ -0,0 +1,5 @@ +module Control.Monad.IO.Effect + ( INFINITY + ) where + +foreign import data INFINITY :: Effect diff --git a/src/Control/Monad/IOSync.purs b/src/Control/Monad/IOSync.purs index 980047b..9c60197 100644 --- a/src/Control/Monad/IOSync.purs +++ b/src/Control/Monad/IOSync.purs @@ -1,5 +1,5 @@ module Control.Monad.IOSync - ( module Control.Monad.IO + ( module Control.Monad.IO.Effect , IOSync(..) ) where @@ -10,7 +10,7 @@ import Control.Monad.Eff.Class (class MonadEff, liftEff) import Control.Monad.Eff.Exception (Error, catchException, error, throwException) import Control.Monad.Eff.Unsafe (unsafeCoerceEff) import Control.Monad.Error.Class (class MonadError, catchError, throwError) -import Control.Monad.IO (INFINITY) +import Control.Monad.IO.Effect (INFINITY) import Control.Monad.Rec.Class (class MonadRec) import Control.MonadZero (class MonadZero) import Control.Plus (class Plus) From 9159e086ef234dcd4706964f563cd7a3d740ca5d Mon Sep 17 00:00:00 2001 From: rightfold Date: Mon, 3 Apr 2017 18:24:33 +0200 Subject: [PATCH 7/8] Add run functions --- src/Control/Monad/IO.purs | 8 ++++++++ src/Control/Monad/IOSync.purs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Control/Monad/IO.purs b/src/Control/Monad/IO.purs index dbd43c2..8bc3a15 100644 --- a/src/Control/Monad/IO.purs +++ b/src/Control/Monad/IO.purs @@ -1,6 +1,8 @@ module Control.Monad.IO ( module Control.Monad.IO.Effect , IO(..) + , runIO + , runIO' , launchIO ) where @@ -24,6 +26,12 @@ import Prelude newtype IO a = IO (Aff (infinity :: INFINITY) a) +runIO :: IO ~> Aff (infinity :: INFINITY) +runIO = unwrap + +runIO' :: ∀ eff. IO ~> Aff (infinity :: INFINITY | eff) +runIO' = unsafeCoerceAff <<< unwrap + launchIO :: ∀ a. IO a -> IOSync Unit launchIO = void <<< liftEff <<< launchAff <<< unwrap diff --git a/src/Control/Monad/IOSync.purs b/src/Control/Monad/IOSync.purs index 9c60197..7d12fd5 100644 --- a/src/Control/Monad/IOSync.purs +++ b/src/Control/Monad/IOSync.purs @@ -1,6 +1,8 @@ module Control.Monad.IOSync ( module Control.Monad.IO.Effect , IOSync(..) + , runIOSync + , runIOSync' ) where import Control.Alt (class Alt) @@ -20,6 +22,12 @@ import Prelude newtype IOSync a = IOSync (Eff (infinity :: INFINITY) a) +runIOSync :: IOSync ~> Eff (infinity :: INFINITY) +runIOSync = unwrap + +runIOSync' :: ∀ eff. IOSync ~> Eff (infinity :: INFINITY | eff) +runIOSync' = unsafeCoerceEff <<< unwrap + derive instance newtypeIOSync :: Newtype (IOSync a) _ derive newtype instance functorIOSync :: Functor IOSync From 92906d0c3696a3dfe61510e419a70bd3c30a688b Mon Sep 17 00:00:00 2001 From: rightfold Date: Mon, 3 Apr 2017 18:25:13 +0200 Subject: [PATCH 8/8] Mention runIO in readme again --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 067d7d1..759227f 100644 --- a/README.md +++ b/README.md @@ -58,16 +58,14 @@ Therefore, MTL and direct-Free approaches can be considered alternatives to Pure `IO` is a newtype for `Aff`, which you can unwrap to be used in your `main`: ```haskell -import Data.Newtype (unwrap) - -unwrap :: forall a. IO a -> Aff (infinity :: INFINITY) a +runIO :: forall a. IO a -> Aff (infinity :: INFINITY) a ``` This converts an `IO` into an `Aff`, which you can then "convert" into a runnable `Eff` using `launchAff` or `runAff`. The effect row is closed, which is intentional because `INFINITY` represents -all possible effects. This will help ensure you only call `unwrap` at the top +all possible effects. This will help ensure you only call `runIO` at the top level of your program. Besides this, `IO` has almost all the same instances as `Aff`, and may be used