{-# OPTIONS -fplugin=Rattus.Plugin #-}
{-# LANGUAGE TypeOperators #-}

-- | Programming with futures, i.e. data that will arrive at some time
-- in the future

module Rattus.Future
  ( map
  , never
  , switch
  , switchTrans
  , whenJust
  , Future(..)
  , await
  , trigger
  , triggerMap
  )

where

import Rattus
import Rattus.Stream hiding (map)

import Prelude hiding (map)


-- | A future may either be available now or later.
data Future a = Now !a | Wait !(O (Future a))

-- all functions in this module are in Rattus 
{-# ANN module Rattus #-}

-- | Apply a function to the value of the future (if it ever occurs).
{-# NOINLINE [1] map #-}
map :: Box (a -> b) -> Future a -> Future b
map :: forall a b. Box (a -> b) -> Future a -> Future b
map Box (a -> b)
f (Now a
x) = forall a. a -> Future a
Now (forall a. Box a -> a
unbox Box (a -> b)
f a
x)
map Box (a -> b)
f (Wait O (Future a)
x) = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay (forall a b. Box (a -> b) -> Future a -> Future b
map Box (a -> b)
f) forall a b. O (a -> b) -> O a -> O b
<#> O (Future a)
x)

-- | A future that will never happen.
never :: Future a
never :: forall a. Future a
never = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay forall a. Future a
never)


-- | @switch s e@ will behave like @s@ until the future @e@ is
-- available with value @s'@, in which case it will behave as @s'@.
switch :: Str a -> Future (Str a) -> Str a
switch :: forall a. Str a -> Future (Str a) -> Str a
switch (a
x ::: O (Str a)
xs) (Wait O (Future (Str a))
fas) = a
x forall a. a -> O (Str a) -> Str a
::: (forall a. a -> O a
delay forall a. Str a -> Future (Str a) -> Str a
switch forall a b. O (a -> b) -> O a -> O b
<#> O (Str a)
xs forall a b. O (a -> b) -> O a -> O b
<#> O (Future (Str a))
fas)
switch Str a
_xs        (Now Str a
ys)   = Str a
ys 

-- | Turn a stream of 'Maybe''s into a future. The future will occur
-- whenever the stream has a value of the form @Just' v@, and the
-- future then has value @v@.
firstJust :: Str (Maybe' a) -> Future a
firstJust :: forall a. Str (Maybe' a) -> Future a
firstJust (Just' a
x ::: O (Str (Maybe' a))
_) = forall a. a -> Future a
Now a
x
firstJust (Maybe' a
Nothing' ::: O (Str (Maybe' a))
xs) = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay forall a. Str (Maybe' a) -> Future a
firstJust forall a b. O (a -> b) -> O a -> O b
<#> O (Str (Maybe' a))
xs)

-- | Turn a stream of 'Maybe''s into a stream of futures. Each such
-- future behaves as if created by 'firstJust'.
whenJust :: Str (Maybe' a) -> Str (Future a)
whenJust :: forall a. Str (Maybe' a) -> Str (Future a)
whenJust cur :: Str (Maybe' a)
cur@(Maybe' a
_ ::: O (Str (Maybe' a))
xs) = forall a. Str (Maybe' a) -> Future a
firstJust Str (Maybe' a)
cur forall a. a -> O (Str a) -> Str a
::: (forall a. a -> O a
delay forall a. Str (Maybe' a) -> Str (Future a)
whenJust forall a b. O (a -> b) -> O a -> O b
<#> O (Str (Maybe' a))
xs)


-- | Like 'switch' but works on stream functions instead of
-- streams. That is, @switchTrans s e@ will behave like @s@ until the
-- future @e@ occurs with value @s'@, in which case it will behave as
-- @s'@.
switchTrans :: (Str a -> Str b) -> Future (Str a -> Str b) -> (Str a -> Str b)
switchTrans :: forall a b.
(Str a -> Str b) -> Future (Str a -> Str b) -> Str a -> Str b
switchTrans Str a -> Str b
f Future (Str a -> Str b)
es Str a
as = forall b a. Str b -> Future (Str a -> Str b) -> Str a -> Str b
switchTrans' (Str a -> Str b
f Str a
as) Future (Str a -> Str b)
es Str a
as

-- | Helper function for 'switchTrans'.
switchTrans' :: Str b -> Future (Str a -> Str b) -> Str a -> Str b
switchTrans' :: forall b a. Str b -> Future (Str a -> Str b) -> Str a -> Str b
switchTrans' (b
x ::: O (Str b)
xs) (Wait O (Future (Str a -> Str b))
fas) (a
_:::O (Str a)
is) = b
x forall a. a -> O (Str a) -> Str a
::: (forall a. a -> O a
delay forall b a. Str b -> Future (Str a -> Str b) -> Str a -> Str b
switchTrans' forall a b. O (a -> b) -> O a -> O b
<#> O (Str b)
xs forall a b. O (a -> b) -> O a -> O b
<#> O (Future (Str a -> Str b))
fas forall a b. O (a -> b) -> O a -> O b
<#> O (Str a)
is)
switchTrans' Str b
_xs        (Now Str a -> Str b
ys)   Str a
is = Str a -> Str b
ys Str a
is

-- | Helper function for 'await'.
await1 :: Stable a => a -> Future b -> Future (a :* b)
await1 :: forall a b. Stable a => a -> Future b -> Future (a :* b)
await1 a
a (Wait O (Future b)
eb) = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay forall a b. Stable a => a -> Future b -> Future (a :* b)
await1 forall a b. Stable a => O (a -> b) -> a -> O b
<## a
a forall a b. O (a -> b) -> O a -> O b
<#> O (Future b)
eb)
await1 a
a (Now  b
b)  = forall a. a -> Future a
Now  (a
a forall a b. a -> b -> a :* b
:* b
b)

-- | Helper function for 'await'.
await2 :: Stable b => b -> Future a -> Future (a :* b)
await2 :: forall b a. Stable b => b -> Future a -> Future (a :* b)
await2 b
b (Wait O (Future a)
ea) = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay forall b a. Stable b => b -> Future a -> Future (a :* b)
await2 forall a b. Stable a => O (a -> b) -> a -> O b
<## b
b forall a b. O (a -> b) -> O a -> O b
<#> O (Future a)
ea)
await2 b
b (Now  a
a)  = forall a. a -> Future a
Now  (a
a forall a b. a -> b -> a :* b
:* b
b)

-- | Synchronise two futures. The resulting future occurs after both
-- futures have occurred (coinciding with whichever future occurred
-- last.
await :: (Stable a, Stable b) => Future a -> Future b -> Future(a :* b)
await :: forall a b.
(Stable a, Stable b) =>
Future a -> Future b -> Future (a :* b)
await (Wait O (Future a)
ea) (Wait O (Future b)
eb)  = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay forall a b.
(Stable a, Stable b) =>
Future a -> Future b -> Future (a :* b)
await forall a b. O (a -> b) -> O a -> O b
<#> O (Future a)
ea forall a b. O (a -> b) -> O a -> O b
<#> O (Future b)
eb)
await (Now a
a)   Future b
eb         = forall a b. Stable a => a -> Future b -> Future (a :* b)
await1 a
a Future b
eb
await Future a
ea        (Now b
b)    = forall b a. Stable b => b -> Future a -> Future (a :* b)
await2 b
b Future a
ea

-- | Trigger a future as soon as the given predicate turns true on the
-- given stream. The value of the future is the same as that of the
-- stream at that time.
trigger :: Box (a -> Bool) -> Str a -> Future a
trigger :: forall a. Box (a -> Bool) -> Str a -> Future a
trigger Box (a -> Bool)
p (a
x ::: O (Str a)
xs)
  | forall a. Box a -> a
unbox Box (a -> Bool)
p a
x  = forall a. a -> Future a
Now a
x
  | Bool
otherwise  = forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay (forall a. Box (a -> Bool) -> Str a -> Future a
trigger Box (a -> Bool)
p) forall a b. O (a -> b) -> O a -> O b
<#> O (Str a)
xs)


-- | Trigger a future as soon as the given function produces a 'Just''
-- value.
triggerMap :: Box (a -> Maybe' b) -> Str a -> Future b
triggerMap :: forall a b. Box (a -> Maybe' b) -> Str a -> Future b
triggerMap Box (a -> Maybe' b)
f (a
x ::: O (Str a)
xs) =
  case forall a. Box a -> a
unbox Box (a -> Maybe' b)
f a
x of
    Just' b
y  -> forall a. a -> Future a
Now b
y
    Maybe' b
Nothing' -> forall a. O (Future a) -> Future a
Wait (forall a. a -> O a
delay (forall a b. Box (a -> Maybe' b) -> Str a -> Future b
triggerMap Box (a -> Maybe' b)
f) forall a b. O (a -> b) -> O a -> O b
<#> O (Str a)
xs)

{-# RULES

  "map/map" forall f g xs.
    map f (map g xs) = map (box (unbox f . unbox g)) xs ;

#-}