rhine-0.1.0.0: src/FRP/Rhine/Schedule.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module FRP.Rhine.Schedule where
-- dunai
import Data.MonadicStreamFunction
-- rhine
import FRP.Rhine.Clock
-- * The schedule type
-- | A schedule implements a combination of two clocks.
-- It outputs a time stamp and an 'Either' value,
-- which specifies which of the two subclocks has ticked.
data Schedule m cl1 cl2
= (TimeDomainOf cl1 ~ TimeDomainOf cl2)
=> Schedule
{ startSchedule
:: cl1 -> cl2
-> m (MSF m () (TimeDomainOf cl1, Either (Tag cl1) (Tag cl2)), TimeDomainOf cl1)
}
-- The type constraint in the constructor is actually useful when pattern matching on 'Schedule',
-- which is interesting since a constraint like 'Monad m' is useful.
-- When reformulating as a GADT, it might get used,
-- but that would mean that we can't use record syntax.
-- * Utilities to create new schedules from existing ones
-- | Lift a schedule along a monad morphism.
hoistSchedule
:: (Monad m1, Monad m2)
=> (forall a . m1 a -> m2 a)
-> Schedule m1 cl1 cl2
-> Schedule m2 cl1 cl2
hoistSchedule hoist Schedule {..} = Schedule startSchedule'
where
startSchedule' cl1 cl2 = hoist
$ first (hoistMSF hoist) <$> startSchedule cl1 cl2
hoistMSF = liftMSFPurer
-- TODO This should be a dunai issue
-- | Swaps the clocks for a given schedule.
flipSchedule
:: Monad m
=> Schedule m cl1 cl2
-> Schedule m cl2 cl1
flipSchedule Schedule {..} = Schedule startSchedule_
where
startSchedule_ cl2 cl1 = first (arr (second swapEither) <<<) <$> startSchedule cl1 cl2
swapEither :: Either a b -> Either b a -- TODO Why is stuff like this not in base? Maybe send pull request...
swapEither (Left a) = Right a
swapEither (Right b) = Left b
-- * Composite clocks
-- | Two clocks can be combined with a schedule as a clock
-- for an asynchronous sequential composition of signal functions.
data SequentialClock m cl1 cl2
= TimeDomainOf cl1 ~ TimeDomainOf cl2
=> SequentialClock
{ sequentialCl1 :: cl1
, sequentialCl2 :: cl2
, sequentialSchedule :: Schedule m cl1 cl2
}
instance (Monad m, Clock m cl1, Clock m cl2)
=> Clock m (SequentialClock m cl1 cl2) where
type TimeDomainOf (SequentialClock m cl1 cl2) = TimeDomainOf cl1
type Tag (SequentialClock m cl1 cl2) = Either (Tag cl1) (Tag cl2)
startClock SequentialClock {..}
= startSchedule sequentialSchedule sequentialCl1 sequentialCl2
-- | Two clocks can be combined with a schedule as a clock
-- for an asynchronous parallel composition of signal functions.
data ParallelClock m cl1 cl2
= TimeDomainOf cl1 ~ TimeDomainOf cl2
=> ParallelClock
{ parallelCl1 :: cl1
, parallelCl2 :: cl2
, parallelSchedule :: Schedule m cl1 cl2
}
instance (Monad m, Clock m cl1, Clock m cl2)
=> Clock m (ParallelClock m cl1 cl2) where
type TimeDomainOf (ParallelClock m cl1 cl2) = TimeDomainOf cl1
type Tag (ParallelClock m cl1 cl2) = Either (Tag cl1) (Tag cl2)
startClock ParallelClock {..}
= startSchedule parallelSchedule parallelCl1 parallelCl2
-- * Navigating the clock tree
-- | The clock that represents the rate at which data enters the system.
type family Leftmost cl where
Leftmost (SequentialClock m cl1 cl2) = Leftmost cl1
Leftmost (ParallelClock m cl1 cl2) = ParallelClock m (Leftmost cl1) (Leftmost cl2)
Leftmost cl = cl
-- | The clock that represents the rate at which data leaves the system.
type family Rightmost cl where
Rightmost (SequentialClock m cl1 cl2) = Rightmost cl2
Rightmost (ParallelClock m cl1 cl2) = ParallelClock m (Rightmost cl1) (Rightmost cl2)
Rightmost cl = cl
-- | A tree representing possible last times to which
-- the constituents of a clock may have ticked.
data LastTime cl where
SequentialLastTime
:: LastTime cl1 -> LastTime cl2
-> LastTime (SequentialClock m cl1 cl2)
ParallelLastTime
:: LastTime cl1 -> LastTime cl2
-> LastTime (ParallelClock m cl1 cl2)
LeafLastTime :: TimeDomainOf cl -> LastTime cl
-- | An inclusion of a clock into a tree of parallel compositions of clocks.
data ParClockInclusion clS cl where
ParClockInL
:: ParClockInclusion (ParallelClock m clL clR) cl
-> ParClockInclusion clL cl
ParClockInR
:: ParClockInclusion (ParallelClock m clL clR) cl
-> ParClockInclusion clR cl
ParClockRefl :: ParClockInclusion cl cl
-- | Generates a tag for the composite clock from a tag of a leaf clock,
-- given a parallel clock inclusion.
parClockTagInclusion :: ParClockInclusion clS cl -> Tag clS -> Tag cl
parClockTagInclusion (ParClockInL parClockInL) tag = parClockTagInclusion parClockInL $ Left tag
parClockTagInclusion (ParClockInR parClockInR) tag = parClockTagInclusion parClockInR $ Right tag
parClockTagInclusion ParClockRefl tag = tag