{-# language Safe #-}

module LazyAsync.Actions.Merge where

import LazyAsync.Types (Complex (..), LazyAsync (..), Outcome (..), Status (..))

merge :: ( Status    a -> Status    b -> Status    c )
             -- ^ Status merge function
      -> ( LazyAsync a -> LazyAsync b -> LazyAsync c )
merge :: (Status a -> Status b -> Status c)
-> LazyAsync a -> LazyAsync b -> LazyAsync c
merge Status a -> Status b -> Status c
(*) LazyAsync a
a LazyAsync b
b = Complex LazyAsync Status c -> LazyAsync c
forall a. Complex LazyAsync Status a -> LazyAsync a
A2 ((Status a -> Status b -> Status c)
-> LazyAsync a -> LazyAsync b -> Complex LazyAsync Status c
forall (f :: * -> *) (g :: * -> *) a x y.
(g x -> g y -> g a) -> f x -> f y -> Complex f g a
Complex Status a -> Status b -> Status c
(*) LazyAsync a
a LazyAsync b
b)
{- ^ A combination of two 'LazyAsync's, where the 'Status' of the
combination is a function of the statuses of each of its parts

🚀 __'LazyAsync.start'__ starts both parts immediately

The behavior of 🕵️ __'LazyAsync.poll'__ and
💣 __'LazyAsync.wait'__ is determined by the status merge function

-}

apply :: LazyAsync (a -> b) -- ^ Left part
      -> LazyAsync a        -- ^ Right part
      -> LazyAsync b        -- ^ Conjunction
apply :: LazyAsync (a -> b) -> LazyAsync a -> LazyAsync b
apply = (Status (a -> b) -> Status a -> Status b)
-> LazyAsync (a -> b) -> LazyAsync a -> LazyAsync b
forall a b c.
(Status a -> Status b -> Status c)
-> LazyAsync a -> LazyAsync b -> LazyAsync c
merge Status (a -> b) -> Status a -> Status b
forall a b. Status (a -> b) -> Status a -> Status b
applyStatus
{- ^
Conjunctively combines the results of two 'LazyAsync's

🚀 __'LazyAsync.start'__ starts both parts immediately

⏸️ __'LazyAsync.wait'__ returns a 'LazyAsync.Success' result after both
parts complete successfully. As soon as one part fails, the whole conjunction
fails immediately (but any 'LazyAsync.Incomplete' part keeps running in the
background)

🕵️ __'LazyAsync.poll'__ returns 'LazyAsync.Failure' if either part has failed;
otherwise 'LazyAsync.Incomplete' if either part has not finished; otherwise
'LazyAsync.Success'

💣 The 'LazyAsync.wait' and 'LazyAsync.poll' operations disclose the
leftmost exception of the parts that have failed so far, which may not
be consistent over time

🌈 'apply' is equivalent to @('merge' 'applyStatus')@
-}

choose :: LazyAsync a -- ^ Left part
       -> LazyAsync a -- ^ Right part
       -> LazyAsync a -- ^ Disjunction
choose :: LazyAsync a -> LazyAsync a -> LazyAsync a
choose = (Status a -> Status a -> Status a)
-> LazyAsync a -> LazyAsync a -> LazyAsync a
forall a b c.
(Status a -> Status b -> Status c)
-> LazyAsync a -> LazyAsync b -> LazyAsync c
merge Status a -> Status a -> Status a
forall a. Status a -> Status a -> Status a
chooseStatus
{- ^
Disjunctively combines the results of two 'LazyAsync's

🚀 __'LazyAsync.start'__ starts both parts immediately

⏸️ __'LazyAsync.wait'__ returns a 'LazyAsync.Success' result after either part
completes successfully. As soon as one part succeeds, the whole disjunction
succeeds immediately (but any 'LazyAsync.Incomplete' part keeps running in the
background)

🕵️ __'LazyAsync.poll'__ returns 'LazyAsync.Success' if either part has
succeeded; otherwise 'LazyAsync.Incomplete' if either part has not finished;
otherwise 'LazyAsync.Failure'

✅ The 'LazyAsync.wait' and 'LazyAsync.poll' operations disclose the leftmost
result of the parts that have succeeded so far, which may not be consistent
over time

🌈 'choose' is equivalent to @('merge' 'chooseStatus')@
-}

{- | Combines two 'LazyAsync.LazyAsync' statuses to produce the status of their
conjunction

💣 Returns the leftmost 'Failure', if there is one

⏳ Otherwise, if any part of a conjunction is 'Incomplete', then the whole thing
evaluates to 'Incomplete'

✅ Only when all parts have completed as 'Success' does the whole succeed

For example, @'applyStatus' 'Incomplete' ('Failure' e)@ = @'Failure' e@ -}
applyStatus :: Status (a -> b) -> Status a -> Status b
applyStatus :: Status (a -> b) -> Status a -> Status b
applyStatus Status (a -> b)
a Status a
b =
    case Status (a -> b)
a of
        Done (Success a -> b
f) ->
            case Status a
b of
                Done (Success a
x) -> Outcome b -> Status b
forall a. Outcome a -> Status a
Done (b -> Outcome b
forall a. a -> Outcome a
Success (a -> b
f a
x))
                Done (Failure SomeException
e) -> Outcome b -> Status b
forall a. Outcome a -> Status a
Done (SomeException -> Outcome b
forall a. SomeException -> Outcome a
Failure SomeException
e)
                Status a
Incomplete       -> Status b
forall a. Status a
Incomplete
        Done (Failure SomeException
e) -> Outcome b -> Status b
forall a. Outcome a -> Status a
Done (SomeException -> Outcome b
forall a. SomeException -> Outcome a
Failure SomeException
e)
        Status (a -> b)
Incomplete ->
            case Status a
b of
                Done (Failure SomeException
e) -> Outcome b -> Status b
forall a. Outcome a -> Status a
Done (SomeException -> Outcome b
forall a. SomeException -> Outcome a
Failure SomeException
e)
                Status a
_                -> Status b
forall a. Status a
Incomplete

{- | Combines two 'LazyAsync.LazyAsync' statuses to produce the status of their
disjunction

✅ Returns the leftmost 'Success', if there is one

⏳ Otherwise, if any part of a disjunction is 'Incomplete', then the whole thing
evaluates to 'Incomplete'

💣 Only when all parts have completed as 'Failure' does the whole fail -}
chooseStatus :: Status a -> Status a -> Status a
chooseStatus :: Status a -> Status a -> Status a
chooseStatus Status a
x Status a
y =
    case Status a
x of
        Done Success{} -> Status a
x
        Done Failure{} -> Status a
y
        Status a
Incomplete ->
            case Status a
y of
                Done Failure{} -> Status a
x
                Status a
_              -> Status a
y

-- | Behaves the same as 'Control.Applicative.<*>' for
-- 'Data.Either.Either', halting at the leftmost 'Failure'
applyOutcome :: Outcome (a -> b) -> Outcome a -> Outcome b
applyOutcome :: Outcome (a -> b) -> Outcome a -> Outcome b
applyOutcome Outcome (a -> b)
fo Outcome a
ao =
    case Outcome (a -> b)
fo of
        Failure SomeException
e -> SomeException -> Outcome b
forall a. SomeException -> Outcome a
Failure SomeException
e
        Success a -> b
f ->
            case Outcome a
ao of
                Failure SomeException
e -> SomeException -> Outcome b
forall a. SomeException -> Outcome a
Failure SomeException
e
                Success a
x -> b -> Outcome b
forall a. a -> Outcome a
Success (a -> b
f a
x)

-- | Behaves the same as 'Control.Applicative.<|>' for
-- 'Data.Either.Either', returning the leftmost 'Success'
chooseOutcome :: Outcome a -> Outcome a -> Outcome a
chooseOutcome :: Outcome a -> Outcome a -> Outcome a
chooseOutcome Outcome a
x Outcome a
y =
    case Outcome a
x of
        Failure{} -> Outcome a
y
        Outcome a
_         -> Outcome a
x