-- |This module provides circuits which are descriptions of reactive systems. module FRP.Grapefruit.Circuit ( Circuit, act, putSetup, create ) where -- Prelude import Prelude (($), (>>), IO, flip, return) import qualified Prelude -- Control import Control.Arrow as Arrow import Control.Arrow.Operations as ArrowOperations import Control.Arrow.Transformer as ArrowTransformer import Control.Arrow.Transformer.Reader as ReaderArrow import Control.Arrow.Transformer.Writer as WriterArrow import Control.Concurrent.MVar as MVar -- Data import Data.Unique as Unique -- FRP.Grapefruit import FRP.Grapefruit.Setup as Setup -- Internal import Internal.Circuit as Circuit {-| This circuit takes an I/O action when it is constructed, performs this action immediately and outputs its result. -} act :: Circuit era (IO output) output act = Circuit $ (lift >>> lift >>> lift) (Kleisli Prelude.id) {-| A circuit which triggers initialization and finalization according to a given setup. -} putSetup :: Circuit era Setup () putSetup = Circuit $ (lift >>> lift) write {-| Creates a circuit. The second argument of @create@ is fed into the circuit as its input and the circuit is constructed then. After that, the initialization actions of all setups inserted by 'putSetup' are run. The finalization actions of the setups are chained and returned by @create@ together with the output of the circuit. Note that initialization is done completely after circuit creation. This allows outputs of circuits to be generated before they are used for forming circuit inputs. This is important to avoid circular dependencies when 'loop' is used. -} create :: (forall era. Circuit era i o) -> i -> IO (o,IO ()) create circuit input = do startTimeID <- newUnique ecFinalizerVar <- newMVar (return ()) (output,setup) <- runCircuitArrow (polyCircuitArrow circuit) startTimeID ecFinalizerVar input finalize <- Setup.run setup return (output,finalize) {- When creating subcircuits because of dynamicity create neither a new EC finalizer variable, nor a new time ID (take the one from the event triggering the subcircuit creation instead). -} polyCircuitArrow :: (forall era. Circuit era input output) -> CircuitArrow input output polyCircuitArrow plainCircuit = circuitArrow plainCircuit circuitArrow :: Circuit era input output -> CircuitArrow input output circuitArrow (Circuit circuitArrow) = circuitArrow runCircuitArrow :: CircuitArrow input output -> Unique -> MVar (IO ()) -> input -> IO (output,Setup) runCircuitArrow circuitArrow startTimeID ecFinalizerVar input = run where run = runKleisli ioArrow input ioArrow = runWriter setupWriterArrow setupWriterArrow = arr (flip (,) ecFinalizerVar) >>> runReader ecFinVarReaderArrow ecFinVarReaderArrow = arr (flip (,) startTimeID) >>> runReader circuitArrow