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

CHG: Generalised AVar errors for compatibility with generalised Aff #18

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
77 changes: 19 additions & 58 deletions src/Effect/AVar.purs
Original file line number Diff line number Diff line change
Expand Up @@ -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
File renamed without changes.
131 changes: 131 additions & 0 deletions src/Effect/AVar/General.purs
Original file line number Diff line number Diff line change
@@ -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
}

40 changes: 17 additions & 23 deletions src/Effect/Aff/AVar.purs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Effect.Aff.AVar
( module Effect.AVar
( module Exports
, new
, empty
, status
Expand All @@ -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
Loading