diff --git a/bower.json b/bower.json index 716099c..2a9d369 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,7 @@ "tests" ], "dependencies": { - "purescript-aff": "^5.0.0", + "purescript-aff": "git@github.com:eric-corumdigital/purescript-aff.git#general_errors", "purescript-effect": "^2.0.0", "purescript-maybe": "^4.0.0", "purescript-either": "^4.0.0", diff --git a/src/Effect/AVar.purs b/src/Effect/AVar.purs index 6084110..42b294b 100644 --- a/src/Effect/AVar.purs +++ b/src/Effect/AVar.purs @@ -18,114 +18,75 @@ module Effect.AVar ) where import Prelude -import Data.Either (Either(..)) -import Data.Function.Uncurried as Fn -import Data.Maybe (Maybe(..)) +import Data.Either (Either) +import Data.Maybe (Maybe) import Effect (Effect) +import Effect.AVar.General as G import Effect.Exception (Error) type AVarCallback a = (Either Error a → Effect Unit) -foreign import data AVar ∷ Type → Type +type AVar a = G.AVar Error a -data AVarStatus a - = Killed Error - | Filled a - | Empty +type AVarStatus a = G.AVarStatus Error a -- | Creates a new empty AVar. -foreign import empty ∷ ∀ a. Effect (AVar a) +empty ∷ ∀ a. Effect (AVar a) +empty = G.empty -- | Creates a fresh AVar with an initial value. new ∷ ∀ a. a → Effect (AVar a) -new = _newVar +new = G.new -- | Kills the AVar with an exception. All pending and future actions will -- | resolve immediately with the provided exception. kill ∷ ∀ a. Error → AVar a → Effect Unit -kill err avar = Fn.runFn3 _killVar ffiUtil err avar +kill = G.kill -- | Sets the value of the AVar. If the AVar is already filled, it will be -- | queued until the value is emptied. Multiple puts will resolve in order as -- | the AVar becomes available. Returns an effect which will remove the -- | callback from the pending queue. put ∷ ∀ a. a → AVar a → AVarCallback Unit → Effect (Effect Unit) -put value avar cb = Fn.runFn4 _putVar ffiUtil value avar cb +put = G.put -- | Attempts to synchronously fill an AVar. If the AVar is already filled, -- | this will do nothing. Returns true or false depending on if it succeeded. tryPut ∷ ∀ a. a → AVar a → Effect Boolean -tryPut value avar = Fn.runFn3 _tryPutVar ffiUtil value avar +tryPut = G.tryPut -- | Takes the AVar value, leaving it empty. If the AVar is already empty, -- | the callback will be queued until the AVar is filled. Multiple takes will -- | resolve in order as the AVar fills. Returns an effect which will remove -- | the callback from the pending queue. take ∷ ∀ a. AVar a → AVarCallback a → Effect (Effect Unit) -take avar cb = Fn.runFn3 _takeVar ffiUtil avar cb +take = G.take -- | Attempts to synchronously take an AVar value, leaving it empty. If the -- | AVar is empty, this will return `Nothing`. tryTake ∷ ∀ a. AVar a → Effect (Maybe a) -tryTake avar = Fn.runFn2 _tryTakeVar ffiUtil avar +tryTake = G.tryTake -- | Reads the AVar value. Unlike `take`, this will not leave the AVar empty. -- | If the AVar is empty, this will queue until it is filled. Multiple reads -- | will resolve at the same time, as soon as possible. read ∷ ∀ a. AVar a → AVarCallback a → Effect (Effect Unit) -read avar cb = Fn.runFn3 _readVar ffiUtil avar cb +read = G.read -- | Attempts to synchronously read an AVar. If the AVar is empty, this will -- | return `Nothing`. tryRead ∷ ∀ a. AVar a → Effect (Maybe a) -tryRead avar = Fn.runFn2 _tryReadVar ffiUtil avar +tryRead = G.tryRead -- | Synchronously checks the status of an AVar. status ∷ ∀ a. AVar a → Effect (AVarStatus a) -status avar = Fn.runFn2 _status ffiUtil avar +status = G.status isEmpty ∷ ∀ a. AVarStatus a → Boolean -isEmpty = case _ of - Empty → true - _ → false +isEmpty = G.isEmpty isFilled ∷ ∀ a. AVarStatus a → Boolean -isFilled = case _ of - Filled _ → true - _ → false +isFilled = G.isFilled isKilled ∷ ∀ a. AVarStatus a → Boolean -isKilled = case _ of - Killed _ → true - _ → false - -foreign import _newVar ∷ ∀ a. a → Effect (AVar a) -foreign import _killVar ∷ ∀ a. Fn.Fn3 FFIUtil Error (AVar a) (Effect Unit) -foreign import _putVar ∷ ∀ a. Fn.Fn4 FFIUtil a (AVar a) (AVarCallback Unit) (Effect (Effect Unit)) -foreign import _tryPutVar ∷ ∀ a. Fn.Fn3 FFIUtil a (AVar a) (Effect Boolean) -foreign import _takeVar ∷ ∀ a. Fn.Fn3 FFIUtil (AVar a) (AVarCallback a) (Effect (Effect Unit)) -foreign import _tryTakeVar ∷ ∀ a. Fn.Fn2 FFIUtil (AVar a) (Effect (Maybe a)) -foreign import _readVar ∷ ∀ a. Fn.Fn3 FFIUtil (AVar a) (AVarCallback a) (Effect (Effect Unit)) -foreign import _tryReadVar ∷ ∀ a. Fn.Fn2 FFIUtil (AVar a) (Effect (Maybe a)) -foreign import _status ∷ ∀ a. Fn.Fn2 FFIUtil (AVar a) (Effect (AVarStatus a)) - -type FFIUtil = - { left ∷ ∀ a b. a → Either a b - , right ∷ ∀ a b. b → Either a b - , nothing ∷ ∀ a. Maybe a - , just ∷ ∀ a. a → Maybe a - , killed ∷ ∀ a. Error → AVarStatus a - , filled ∷ ∀ a. a → AVarStatus a - , empty ∷ ∀ a. AVarStatus a - } - -ffiUtil ∷ FFIUtil -ffiUtil = - { left: Left - , right: Right - , nothing: Nothing - , just: Just - , killed: Killed - , filled: Filled - , empty: Empty - } +isKilled = G.isKilled diff --git a/src/Effect/AVar.js b/src/Effect/AVar/General.js similarity index 100% rename from src/Effect/AVar.js rename to src/Effect/AVar/General.js diff --git a/src/Effect/AVar/General.purs b/src/Effect/AVar/General.purs new file mode 100644 index 0000000..35e06cd --- /dev/null +++ b/src/Effect/AVar/General.purs @@ -0,0 +1,131 @@ +module Effect.AVar.General + ( AVar + , AVarCallback + , AVarStatus(..) + , new + , empty + , take + , tryTake + , put + , tryPut + , read + , tryRead + , kill + , status + , isEmpty + , isFilled + , isKilled + ) where + +import Data.Either (Either(..)) +import Data.Function.Uncurried as Fn +import Data.Maybe (Maybe(..)) +import Effect (Effect) +import Prelude (Unit) + +type AVarCallback e a = (Either e a → Effect Unit) + +foreign import data AVar ∷ Type → Type → Type + +data AVarStatus e a + = Killed e + | Filled a + | Empty + +-- | Creates a new empty AVar. +foreign import empty ∷ ∀ e a. Effect (AVar e a) + +-- | Creates a fresh AVar with an initial value. +new ∷ ∀ e a. a → Effect (AVar e a) +new = _newVar + +-- | Kills the AVar with an exception. All pending and future actions will +-- | resolve immediately with the provided exception. +kill ∷ ∀ e a. e → AVar e a → Effect Unit +kill err avar = Fn.runFn3 _killVar ffiUtil err avar + +-- | Sets the value of the AVar. If the AVar is already filled, it will be +-- | queued until the value is emptied. Multiple puts will resolve in order as +-- | the AVar becomes available. Returns an effect which will remove the +-- | callback from the pending queue. +put ∷ ∀ e a. a → AVar e a → AVarCallback e Unit → Effect (Effect Unit) +put value avar cb = Fn.runFn4 _putVar ffiUtil value avar cb + +-- | Attempts to synchronously fill an AVar. If the AVar is already filled, +-- | this will do nothing. Returns true or false depending on if it succeeded. +tryPut ∷ ∀ e a. a → AVar e a → Effect Boolean +tryPut value avar = Fn.runFn3 _tryPutVar ffiUtil value avar + +-- | Takes the AVar value, leaving it empty. If the AVar is already empty, +-- | the callback will be queued until the AVar is filled. Multiple takes will +-- | resolve in order as the AVar fills. Returns an effect which will remove +-- | the callback from the pending queue. +take ∷ ∀ e a. AVar e a → AVarCallback e a → Effect (Effect Unit) +take avar cb = Fn.runFn3 _takeVar ffiUtil avar cb + +-- | Attempts to synchronously take an AVar value, leaving it empty. If the +-- | AVar is empty, this will return `Nothing`. +tryTake ∷ ∀ e a. AVar e a → Effect (Maybe a) +tryTake avar = Fn.runFn2 _tryTakeVar ffiUtil avar + +-- | Reads the AVar value. Unlike `take`, this will not leave the AVar empty. +-- | If the AVar is empty, this will queue until it is filled. Multiple reads +-- | will resolve at the same time, as soon as possible. +read ∷ ∀ e a. AVar e a → AVarCallback e a → Effect (Effect Unit) +read avar cb = Fn.runFn3 _readVar ffiUtil avar cb + +-- | Attempts to synchronously read an AVar. If the AVar is empty, this will +-- | return `Nothing`. +tryRead ∷ ∀ e a. AVar e a → Effect (Maybe a) +tryRead avar = Fn.runFn2 _tryReadVar ffiUtil avar + +-- | Synchronously checks the status of an AVar. +status ∷ ∀ e a. AVar e a → Effect (AVarStatus e a) +status avar = Fn.runFn2 _status ffiUtil avar + +isEmpty ∷ ∀ e a. AVarStatus e a → Boolean +isEmpty = case _ of + Empty → true + _ → false + +isFilled ∷ ∀ e a. AVarStatus e a → Boolean +isFilled = case _ of + Filled _ → true + _ → false + +isKilled ∷ ∀ e a. AVarStatus e a → Boolean +isKilled = case _ of + Killed _ → true + _ → false + +foreign import _newVar ∷ ∀ e a. a → Effect (AVar e a) +foreign import _killVar ∷ ∀ e a. Fn.Fn3 FFIUtil e (AVar e a) (Effect Unit) +foreign import _putVar ∷ ∀ e a. Fn.Fn4 FFIUtil a (AVar e a) (AVarCallback e Unit) (Effect (Effect Unit)) +foreign import _tryPutVar ∷ ∀ e a. Fn.Fn3 FFIUtil a (AVar e a) (Effect Boolean) +foreign import _takeVar ∷ ∀ e a. Fn.Fn3 FFIUtil (AVar e a) (AVarCallback e a) (Effect (Effect Unit)) +foreign import _tryTakeVar ∷ ∀ e a. Fn.Fn2 FFIUtil (AVar e a) (Effect (Maybe a)) +foreign import _readVar ∷ ∀ e a. Fn.Fn3 FFIUtil (AVar e a) (AVarCallback e a) (Effect (Effect Unit)) +foreign import _tryReadVar ∷ ∀ e a. Fn.Fn2 FFIUtil (AVar e a) (Effect (Maybe a)) +foreign import _status ∷ ∀ e a. Fn.Fn2 FFIUtil (AVar e a) (Effect (AVarStatus e a)) + +type FFIUtil = + { left ∷ ∀ a b. a → Either a b + , right ∷ ∀ a b. b → Either a b + , nothing ∷ ∀ a. Maybe a + , just ∷ ∀ a. a → Maybe a + , killed ∷ ∀ e a. e → AVarStatus e a + , filled ∷ ∀ e a. a → AVarStatus e a + , empty ∷ ∀ e a. AVarStatus e a + } + +ffiUtil ∷ FFIUtil +ffiUtil = + { left: Left + , right: Right + , nothing: Nothing + , just: Just + , killed: Killed + , filled: Filled + , empty: Empty + } + diff --git a/src/Effect/Aff/AVar.purs b/src/Effect/Aff/AVar.purs index 8344280..03140c0 100644 --- a/src/Effect/Aff/AVar.purs +++ b/src/Effect/Aff/AVar.purs @@ -1,5 +1,5 @@ module Effect.Aff.AVar - ( module Effect.AVar + ( module Exports , new , empty , status @@ -12,66 +12,60 @@ module Effect.Aff.AVar , kill ) where -import Prelude import Data.Maybe (Maybe) -import Effect.Aff (Aff, makeAff, effectCanceler) -import Effect.AVar (AVar, AVarStatus(..), isEmpty, isFilled, isKilled) -import Effect.AVar as AVar -import Effect.Class (liftEffect) +import Effect.AVar (AVar, AVarStatus) +import Effect.AVar (AVar, AVarStatus) as Exports +import Effect.Aff (Aff) +import Effect.Aff.AVar.General as G import Effect.Exception (Error) +import Prelude (Unit) -- | Creates a fresh AVar with an initial value. new ∷ ∀ a. a → Aff (AVar a) -new = liftEffect <<< AVar.new +new = G.new -- | Creates a fresh AVar. empty ∷ ∀ a. Aff (AVar a) -empty = liftEffect AVar.empty +empty = G.empty -- | Synchronously checks the status of an AVar. -status ∷ ∀ a. AVar a → Aff (AVar.AVarStatus a) -status = liftEffect <<< AVar.status +status ∷ ∀ a. AVar a → Aff (AVarStatus a) +status = G.status -- | Takes the AVar value, leaving it empty. If the AVar is already empty, -- | the callback will be queued until the AVar is filled. Multiple takes will -- | resolve in order as the AVar fills. take ∷ ∀ a. AVar a → Aff a -take avar = makeAff \k → do - c ← AVar.take avar k - pure (effectCanceler c) +take = G.take -- | Attempts to synchronously take an AVar value, leaving it empty. If the -- | AVar is empty, this will return `Nothing`. tryTake ∷ ∀ a. AVar a → Aff (Maybe a) -tryTake = liftEffect <<< AVar.tryTake +tryTake = G.tryTake -- | Sets the value of the AVar. If the AVar is already filled, it will be -- | queued until the value is emptied. Multiple puts will resolve in order as -- | the AVar becomes available. put ∷ ∀ a. a → AVar a → Aff Unit -put value avar = makeAff \k → do - c ← AVar.put value avar k - pure (effectCanceler c) +put = G.put -- | Attempts to synchronously fill an AVar. If the AVar is already filled, -- | this will do nothing. Returns true or false depending on if it succeeded. tryPut ∷ ∀ a. a → AVar a → Aff Boolean -tryPut value = liftEffect <<< AVar.tryPut value +tryPut = G.tryPut -- | Reads the AVar value. Unlike `take`, this will not leave the AVar empty. -- | If the AVar is empty, this will queue until it is filled. Multiple reads -- | will resolve at the same time, as soon as possible. read ∷ ∀ a. AVar a → Aff a -read avar = makeAff \k → do - c ← AVar.read avar k - pure (effectCanceler c) +read = G.read -- | Attempts to synchronously read an AVar. If the AVar is empty, this will -- | return `Nothing`. tryRead ∷ ∀ a. AVar a → Aff (Maybe a) -tryRead = liftEffect <<< AVar.tryRead +tryRead = G.tryRead -- | Kills the AVar with an exception. All pending and future actions will -- | resolve immediately with the provided exception. kill ∷ ∀ a. Error → AVar a → Aff Unit -kill error = liftEffect <<< AVar.kill error +kill = G.kill diff --git a/src/Effect/Aff/AVar/General.purs b/src/Effect/Aff/AVar/General.purs new file mode 100644 index 0000000..2562a01 --- /dev/null +++ b/src/Effect/Aff/AVar/General.purs @@ -0,0 +1,78 @@ +module Effect.Aff.AVar.General + ( module Exports + , new + , empty + , status + , take + , tryTake + , put + , tryPut + , read + , tryRead + , kill + ) where + +import Data.Either (either) +import Data.Maybe (Maybe) +import Effect.AVar.General (AVar, AVarStatus(..), AVarCallback) as Exports +import Effect.AVar.General (AVar, AVarStatus) +import Effect.AVar.General as AVar +import Effect.Aff.General (Aff, AffResult(..), effectCanceler, makeAff) +import Effect.Class (liftEffect) +import Prelude (Unit, bind, pure, (<<<)) + +-- | Creates a fresh AVar with an initial value. +new ∷ ∀ e1 e2 a. a → Aff e1 (AVar e2 a) +new = liftEffect <<< AVar.new + +-- | Creates a fresh AVar. +empty ∷ ∀ e1 e2 a. Aff e1 (AVar e2 a) +empty = liftEffect AVar.empty + +-- | Synchronously checks the status of an AVar. +status ∷ ∀ e1 e2 a. AVar e1 a → Aff e2 (AVarStatus e1 a) +status = liftEffect <<< AVar.status + +-- | Takes the AVar value, leaving it empty. If the AVar is already empty, +-- | the callback will be queued until the AVar is filled. Multiple takes will +-- | resolve in order as the AVar fills. +take ∷ ∀ e a. AVar e a → Aff e a +take avar = makeAff \k → do + c ← AVar.take avar (k <<< either Failed Succeeded) + pure (effectCanceler c) + +-- | Attempts to synchronously take an AVar value, leaving it empty. If the +-- | AVar is empty, this will return `Nothing`. +tryTake ∷ ∀ e1 e2 a. AVar e1 a → Aff e2 (Maybe a) +tryTake = liftEffect <<< AVar.tryTake + +-- | Sets the value of the AVar. If the AVar is already filled, it will be +-- | queued until the value is emptied. Multiple puts will resolve in order as +-- | the AVar becomes available. +put ∷ ∀ e a. a → AVar e a → Aff e Unit +put value avar = makeAff \k → do + c ← AVar.put value avar (k <<< either Failed Succeeded) + pure (effectCanceler c) + +-- | Attempts to synchronously fill an AVar. If the AVar is already filled, +-- | this will do nothing. Returns true or false depending on if it succeeded. +tryPut ∷ ∀ e1 e2 a. a → AVar e1 a → Aff e2 Boolean +tryPut value = liftEffect <<< AVar.tryPut value + +-- | Reads the AVar value. Unlike `take`, this will not leave the AVar empty. +-- | If the AVar is empty, this will queue until it is filled. Multiple reads +-- | will resolve at the same time, as soon as possible. +read ∷ ∀ e a. AVar e a → Aff e a +read avar = makeAff \k → do + c ← AVar.read avar (k <<< either Failed Succeeded) + pure (effectCanceler c) + +-- | Attempts to synchronously read an AVar. If the AVar is empty, this will +-- | return `Nothing`. +tryRead ∷ ∀ e1 e2 a. AVar e1 a → Aff e2 (Maybe a) +tryRead = liftEffect <<< AVar.tryRead + +-- | Kills the AVar with an exception. All pending and future actions will +-- | resolve immediately with the provided exception. +kill ∷ ∀ e1 e2 a. e1 → AVar e1 a → Aff e2 Unit +kill error = liftEffect <<< AVar.kill error