{-# LANGUAGE CPP        #-}
{-# LANGUAGE Rank2Types #-}
-- |
-- Module      :  FRP.Yampa.Task
-- Copyright   :  (c) Antony Courtney and Henrik Nilsson, Yale University, 2003
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  ivan.perez@keera.co.uk
-- Stability   :  provisional
-- Portability :  non-portable (GHC extensions)
--
-- Task abstraction on top of signal transformers.
module FRP.Yampa.Task (
    Task,
    mkTask,      -- :: SF a (b, Event c) -> Task a b c
    runTask,     -- :: Task a b c -> SF a (Either b c)    -- Might change.
    runTask_,    -- :: Task a b c -> SF a b
    taskToSF,    -- :: Task a b c -> SF a (b, Event c)    -- Might change.
    constT,      -- :: b -> Task a b c
    sleepT,      -- :: Time -> b -> Task a b ()
    snapT,       -- :: Task a b a
    timeOut,     -- :: Task a b c -> Time -> Task a b (Maybe c)
    abortWhen,   -- :: Task a b c -> SF a (Event d) -> Task a b (Either c d)
) where

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative (Applicative(..))
#endif

import FRP.Yampa.Basic        (constant)
import FRP.Yampa.Diagnostics  (intErr, usrErr)
import FRP.Yampa.Event        (Event, lMerge)
import FRP.Yampa.EventS       (after, edgeBy, never, snap)
import FRP.Yampa.InternalCore (SF, Time, arr, first, (&&&), (>>>))
import FRP.Yampa.Switches     (switch)

infixl 0 `timeOut`, `abortWhen`


-- * The Task type


-- | A task is a partially SF that may terminate with a result.

newtype Task a b c =
    -- CPS-based representation allowing termination to be detected.
    -- (Note the rank 2 polymorphic type!)
    -- The representation can be changed if necessary, but the Monad laws
    -- follow trivially in this case.
    Task (forall d . (c -> SF a (Either b d)) -> SF a (Either b d))

unTask :: Task a b c -> ((c -> SF a (Either b d)) -> SF a (Either b d))
unTask :: Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask (Task forall d. (c -> SF a (Either b d)) -> SF a (Either b d)
f) = (c -> SF a (Either b d)) -> SF a (Either b d)
forall d. (c -> SF a (Either b d)) -> SF a (Either b d)
f

-- | Creates a 'Task' from an SF that returns, as a second output, an 'Event'
-- when the SF terminates. See 'switch'.
mkTask :: SF a (b, Event c) -> Task a b c
mkTask :: SF a (b, Event c) -> Task a b c
mkTask SF a (b, Event c)
st = (forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
forall a b c.
(forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
Task (SF a (Either b d, Event c)
-> (c -> SF a (Either b d)) -> SF a (Either b d)
forall a b c. SF a (b, Event c) -> (c -> SF a b) -> SF a b
switch (SF a (b, Event c)
st SF a (b, Event c)
-> SF (b, Event c) (Either b d, Event c)
-> SF a (Either b d, Event c)
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> SF b (Either b d) -> SF (b, Event c) (Either b d, Event c)
forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
first ((b -> Either b d) -> SF b (Either b d)
forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr b -> Either b d
forall a b. a -> Either a b
Left)))


-- | Runs a task.
--
-- The output from the resulting signal transformer is tagged with Left while
-- the underlying task is running. Once the task has terminated, the output
-- goes constant with the value Right x, where x is the value of the
-- terminating event.

-- Check name.
runTask :: Task a b c -> SF a (Either b c)
runTask :: Task a b c -> SF a (Either b c)
runTask Task a b c
tk = (Task a b c -> (c -> SF a (Either b c)) -> SF a (Either b c)
forall a b c d.
Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask Task a b c
tk) (Either b c -> SF a (Either b c)
forall b a. b -> SF a b
constant (Either b c -> SF a (Either b c))
-> (c -> Either b c) -> c -> SF a (Either b c)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. c -> Either b c
forall a b. b -> Either a b
Right)


-- | Runs a task that never terminates.
--
-- The output becomes undefined once the underlying task has terminated.
--
-- Convenience function for tasks which are known not to terminate.
runTask_ :: Task a b c -> SF a b
runTask_ :: Task a b c -> SF a b
runTask_ Task a b c
tk = Task a b c -> SF a (Either b c)
forall a b c. Task a b c -> SF a (Either b c)
runTask Task a b c
tk
              SF a (Either b c) -> SF (Either b c) b -> SF a b
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (Either b c -> b) -> SF (Either b c) b
forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr ((b -> b) -> (c -> b) -> Either b c -> b
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either b -> b
forall a. a -> a
id (String -> String -> String -> c -> b
forall a. String -> String -> String -> a
usrErr String
"AFRPTask" String
"runTask_"
                                         String
"Task terminated!"))


-- | Creates an SF that represents an SF and produces an event
-- when the task terminates, and otherwise produces just an output.
taskToSF :: Task a b c -> SF a (b, Event c)
taskToSF :: Task a b c -> SF a (b, Event c)
taskToSF Task a b c
tk = Task a b c -> SF a (Either b c)
forall a b c. Task a b c -> SF a (Either b c)
runTask Task a b c
tk
              SF a (Either b c)
-> SF (Either b c) (b, Event c) -> SF a (b, Event c)
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> ((Either b c -> b) -> SF (Either b c) b
forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr ((b -> b) -> (c -> b) -> Either b c -> b
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either b -> b
forall a. a -> a
id (String -> String -> String -> c -> b
forall a. String -> String -> String -> a
usrErr String
"AFRPTask" String
"runTask_"
                                          String
"Task terminated!"))
                   SF (Either b c) b
-> SF (Either b c) (Event c) -> SF (Either b c) (b, Event c)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& (Either b c -> Either b c -> Maybe c)
-> Either b c -> SF (Either b c) (Event c)
forall a b. (a -> a -> Maybe b) -> a -> SF a (Event b)
edgeBy Either b c -> Either b c -> Maybe c
forall a b a a. Either a b -> Either a a -> Maybe a
isEdge (b -> Either b c
forall a b. a -> Either a b
Left b
forall a. HasCallStack => a
undefined))
    where
        isEdge :: Either a b -> Either a a -> Maybe a
isEdge (Left a
_)  (Left a
_)  = Maybe a
forall a. Maybe a
Nothing
        isEdge (Left a
_)  (Right a
c) = a -> Maybe a
forall a. a -> Maybe a
Just a
c
        isEdge (Right b
_) (Right a
_) = Maybe a
forall a. Maybe a
Nothing
        isEdge (Right b
_) (Left a
_)  = Maybe a
forall a. Maybe a
Nothing


-- * Functor, Applicative and Monad instance

instance Functor (Task a b) where
    fmap :: (a -> b) -> Task a b a -> Task a b b
fmap a -> b
f Task a b a
tk = (forall d. (b -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b b
forall a b c.
(forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
Task (\b -> SF a (Either b d)
k -> Task a b a -> (a -> SF a (Either b d)) -> SF a (Either b d)
forall a b c d.
Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask Task a b a
tk (b -> SF a (Either b d)
k (b -> SF a (Either b d)) -> (a -> b) -> a -> SF a (Either b d)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
f))

instance Applicative (Task a b) where
    pure :: a -> Task a b a
pure a
x  = (forall d. (a -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b a
forall a b c.
(forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
Task (\a -> SF a (Either b d)
k -> a -> SF a (Either b d)
k a
x)
    Task a b (a -> b)
f <*> :: Task a b (a -> b) -> Task a b a -> Task a b b
<*> Task a b a
v = (forall d. (b -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b b
forall a b c.
(forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
Task (\b -> SF a (Either b d)
k -> (Task a b (a -> b)
-> ((a -> b) -> SF a (Either b d)) -> SF a (Either b d)
forall a b c d.
Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask Task a b (a -> b)
f) (\a -> b
c -> Task a b a -> (a -> SF a (Either b d)) -> SF a (Either b d)
forall a b c d.
Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask Task a b a
v (b -> SF a (Either b d)
k (b -> SF a (Either b d)) -> (a -> b) -> a -> SF a (Either b d)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> b
c)))

instance Monad (Task a b) where
    Task a b a
tk >>= :: Task a b a -> (a -> Task a b b) -> Task a b b
>>= a -> Task a b b
f = (forall d. (b -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b b
forall a b c.
(forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
Task (\b -> SF a (Either b d)
k -> Task a b a -> (a -> SF a (Either b d)) -> SF a (Either b d)
forall a b c d.
Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask Task a b a
tk (\a
c -> Task a b b -> (b -> SF a (Either b d)) -> SF a (Either b d)
forall a b c d.
Task a b c -> (c -> SF a (Either b d)) -> SF a (Either b d)
unTask (a -> Task a b b
f a
c) b -> SF a (Either b d)
k))
    return :: a -> Task a b a
return a
x = (forall d. (a -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b a
forall a b c.
(forall d. (c -> SF a (Either b d)) -> SF a (Either b d))
-> Task a b c
Task (\a -> SF a (Either b d)
k -> a -> SF a (Either b d)
k a
x)

-- Let's check the monad laws:
--
--     t >>= return
--     = \k -> t (\c -> return c k)
--     = \k -> t (\c -> (\x -> \k -> k x) c k)
--     = \k -> t (\c -> (\x -> \k' -> k' x) c k)
--     = \k -> t (\c -> k c)
--     = \k -> t k
--     = t
--     QED
--
--     return x >>= f
--     = \k -> (return x) (\c -> f c k)
--     = \k -> (\k -> k x) (\c -> f c k)
--     = \k -> (\k' -> k' x) (\c -> f c k)
--     = \k -> (\c -> f c k) x
--     = \k -> f x k
--     = f x
--     QED
--
--     (t >>= f) >>= g
--     = \k -> (t >>= f) (\c -> g c k)
--     = \k -> (\k' -> t (\c' -> f c' k')) (\c -> g c k)
--     = \k -> t (\c' -> f c' (\c -> g c k))
--     = \k -> t (\c' -> (\x -> \k' -> f x (\c -> g c k')) c' k)
--     = \k -> t (\c' -> (\x -> f x >>= g) c' k)
--     = t >>= (\x -> f x >>= g)
--     QED
--
-- No surprises (obviously, since this is essentially just the CPS monad).

-- * Basic tasks

-- | Non-terminating task with constant output b.
constT :: b -> Task a b c
constT :: b -> Task a b c
constT b
b = SF a (b, Event c) -> Task a b c
forall a b c. SF a (b, Event c) -> Task a b c
mkTask (b -> SF a b
forall b a. b -> SF a b
constant b
b SF a b -> SF a (Event c) -> SF a (b, Event c)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& SF a (Event c)
forall a b. SF a (Event b)
never)


-- | "Sleeps" for t seconds with constant output b.
sleepT :: Time -> b -> Task a b ()
sleepT :: Time -> b -> Task a b ()
sleepT Time
t b
b = SF a (b, Event ()) -> Task a b ()
forall a b c. SF a (b, Event c) -> Task a b c
mkTask (b -> SF a b
forall b a. b -> SF a b
constant b
b SF a b -> SF a (Event ()) -> SF a (b, Event ())
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Time -> () -> SF a (Event ())
forall b a. Time -> b -> SF a (Event b)
after Time
t ())


-- | Takes a "snapshot" of the input and terminates immediately with the input
-- value as the result.
--
-- No time passes; therefore, the following must hold:
--
-- @snapT >> snapT = snapT@

snapT :: Task a b a
snapT :: Task a b a
snapT = SF a (b, Event a) -> Task a b a
forall a b c. SF a (b, Event c) -> Task a b c
mkTask (b -> SF a b
forall b a. b -> SF a b
constant (String -> String -> String -> b
forall a. String -> String -> String -> a
intErr String
"AFRPTask" String
"snapT" String
"Bad switch?") SF a b -> SF a (Event a) -> SF a (b, Event a)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& SF a (Event a)
forall a. SF a (Event a)
snap)


-- * Basic tasks combinators

-- | Impose a time out on a task.
timeOut :: Task a b c -> Time -> Task a b (Maybe c)
Task a b c
tk timeOut :: Task a b c -> Time -> Task a b (Maybe c)
`timeOut` Time
t = SF a (b, Event (Maybe c)) -> Task a b (Maybe c)
forall a b c. SF a (b, Event c) -> Task a b c
mkTask ((Task a b c -> SF a (b, Event c)
forall a b c. Task a b c -> SF a (b, Event c)
taskToSF Task a b c
tk SF a (b, Event c)
-> SF a (Event ()) -> SF a ((b, Event c), Event ())
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& Time -> () -> SF a (Event ())
forall b a. Time -> b -> SF a (Event b)
after Time
t ()) SF a ((b, Event c), Event ())
-> SF ((b, Event c), Event ()) (b, Event (Maybe c))
-> SF a (b, Event (Maybe c))
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (((b, Event c), Event ()) -> (b, Event (Maybe c)))
-> SF ((b, Event c), Event ()) (b, Event (Maybe c))
forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr ((b, Event c), Event ()) -> (b, Event (Maybe c))
forall a a b. ((a, Event a), Event b) -> (a, Event (Maybe a))
aux)
    where
        aux :: ((a, Event a), Event b) -> (a, Event (Maybe a))
aux ((a
b, Event a
ec), Event b
et) = (a
b, (Event (Maybe a) -> Event (Maybe a) -> Event (Maybe a)
forall a. Event a -> Event a -> Event a
lMerge ((a -> Maybe a) -> Event a -> Event (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just Event a
ec)
                                 ((b -> Maybe a) -> Event b -> Event (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Maybe a -> b -> Maybe a
forall a b. a -> b -> a
const Maybe a
forall a. Maybe a
Nothing) Event b
et)))

-- | Run a "guarding" event source (SF a (Event b)) in parallel with a
-- (possibly non-terminating) task.
--
-- The task will be aborted at the first occurrence of the event source (if it
-- has not terminated itself before that).
--
-- Useful for separating sequencing and termination concerns.  E.g. we can do
-- something "useful", but in parallel watch for a (exceptional) condition
-- which should terminate that activity, without having to check for that
-- condition explicitly during each and every phase of the activity.
--
-- Example: @tsk `abortWhen` lbp@
abortWhen :: Task a b c -> SF a (Event d) -> Task a b (Either c d)
Task a b c
tk abortWhen :: Task a b c -> SF a (Event d) -> Task a b (Either c d)
`abortWhen` SF a (Event d)
est = SF a (b, Event (Either c d)) -> Task a b (Either c d)
forall a b c. SF a (b, Event c) -> Task a b c
mkTask ((Task a b c -> SF a (b, Event c)
forall a b c. Task a b c -> SF a (b, Event c)
taskToSF Task a b c
tk SF a (b, Event c) -> SF a (Event d) -> SF a ((b, Event c), Event d)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& SF a (Event d)
est) SF a ((b, Event c), Event d)
-> SF ((b, Event c), Event d) (b, Event (Either c d))
-> SF a (b, Event (Either c d))
forall k (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (((b, Event c), Event d) -> (b, Event (Either c d)))
-> SF ((b, Event c), Event d) (b, Event (Either c d))
forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr ((b, Event c), Event d) -> (b, Event (Either c d))
forall a a b. ((a, Event a), Event b) -> (a, Event (Either a b))
aux)
    where
        aux :: ((a, Event a), Event b) -> (a, Event (Either a b))
aux ((a
b, Event a
ec), Event b
ed) = (a
b, (Event (Either a b) -> Event (Either a b) -> Event (Either a b)
forall a. Event a -> Event a -> Event a
lMerge ((a -> Either a b) -> Event a -> Event (Either a b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Either a b
forall a b. a -> Either a b
Left Event a
ec) ((b -> Either a b) -> Event b -> Event (Either a b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap b -> Either a b
forall a b. b -> Either a b
Right Event b
ed)))