{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE Rank2Types     #-}
-- | Monadic Stream Functions are synchronized stream functions
--   with side effects.
--
--   MSFs are defined by a function @unMSF :: MSF m a b -> a -> m (b, MSF m a b)@
--   that executes one step of a simulation, and produces an output in a
--   monadic context, and a continuation to be used for future steps.
--
--   MSFs are a generalisation of the implementation mechanism used by Yampa,
--   Wormholes and other FRP and reactive implementations.
--
--   When combined with different monads, they produce interesting effects. For
--   example, when combined with the @Maybe@ monad, they become transformations
--   that may stop producing outputs (and continuations). The @Either@ monad
--   gives rise to MSFs that end with a result (akin to Tasks in Yampa, and
--   Monadic FRP).
--
--   Flattening, that is, going from some structure @MSF (t m) a b@ to @MSF m a b@
--   for a specific transformer @t@ often gives rise to known FRP constructs.
--   For instance, flattening with @EitherT@ gives rise to switching, and
--   flattening with @ListT@ gives rise to parallelism with broadcasting.
--
--   MSFs can be used to implement many FRP variants, including Arrowized FRP,
--   Classic FRP, and plain reactive programming. Arrowized and applicative
--   syntax are both supported.
--
--   For a very detailed introduction to MSFs, see:
--   <http://dl.acm.org/citation.cfm?id=2976010>
--   (mirror: <http://www.cs.nott.ac.uk/~psxip1/#FRPRefactored>).

-- NOTE TO IMPLEMENTORS:
--
-- This module contains the core. Only the core. It should be possible
-- to define every function and type outside this module, except for the
-- instances for ArrowLoop, ArrowChoice, etc., without access to the
-- internal constructor for MSF and the function 'unMSF'.
--
-- It's very hard to know what IS essential to framework and if we start
-- adding all the functions and instances that *may* be useful in one
-- module.
--
-- By separating some instances and functions in other modules , we can
-- easily understand what is the essential idea and then analyse how it
-- is affected by an extension. It also helps demonstrate that something
-- works for MSFs + ArrowChoice, or MSFs + ArrowLoop, etc.
--
-- To address potential violations of basic design principles (like 'not
-- having orphan instances'), the main module Data.MonadicStreamFunction
-- exports everything. Users should *never* import this module here
-- individually, but the main module instead.
module Data.MonadicStreamFunction.Core where

-- External
import Control.Arrow
import Control.Applicative
import Control.Category (Category(..))
import Control.Monad
import Control.Monad.Base
import Control.Monad.Trans.Class
import Prelude hiding ((.), id, sum)

-- * Definitions

-- | Stepwise, side-effectful MSFs without implicit knowledge of time.
--
-- MSFs should be applied to streams or executed indefinitely or until they
-- terminate. See 'reactimate' and 'reactimateB' for details. In general,
-- calling the value constructor 'MSF' or the function 'unMSF' is discouraged.
data MSF m a b = MSF { unMSF :: a -> m (b, MSF m a b) }

-- Instances

instance Monad m => Category (MSF m) where
  id = go
    where go = MSF $ \a -> return (a, go)
  sf2 . sf1 = MSF $ \a -> do
    (b, sf1') <- unMSF sf1 a
    (c, sf2') <- unMSF sf2 b
    let sf' = sf2' . sf1'
    c `seq` return (c, sf')

instance Monad m => Arrow (MSF m) where

  arr f = go
    where go = MSF $ \a -> return (f a, go)

  first sf = MSF $ \(a,c) -> do
    (b, sf') <- unMSF sf a
    b `seq` return ((b, c), first sf')

instance Functor m => Functor (MSF m a) where
  -- fmap f msf == msf >>> arr f
  fmap f msf = MSF $ fmap fS . unMSF msf
    where
      fS (b, cont) = (f b, fmap f cont)

instance (Functor m, Monad m) => Applicative (MSF m a) where
  -- It is possible to define this instance with only Applicative m
  pure = arr . const
  fs <*> bs = (fs &&& bs) >>> arr (uncurry ($))

-- * Monadic computations and MSFs

-- ** Lifting point-wise computations

-- | Apply a monadic transformation to every element of the input stream.
--
-- Generalisation of 'arr' from 'Arrow' to monadic functions.
arrM :: Monad m => (a -> m b) -> MSF m a b
arrM f = go
  where go = MSF $ \a -> do
               b <- f a
               return (b, go)

-- | Monadic lifting from one monad into another
liftS :: (Monad m2, MonadBase m1 m2) => (a -> m1 b) -> MSF m2 a b
liftS = arrM . (liftBase .)

-- ** Lifting MSFs

-- *** Lifting across monad stacks

-- | Lift inner monadic actions in monad stacks.

-- TODO Should be able to express this in terms of MonadBase
liftMSFTrans :: (MonadTrans t, Monad m, Monad (t m))
             => MSF m a b
             -> MSF (t m) a b
liftMSFTrans sf = MSF $ \a -> do
  (b, sf') <- lift $ unMSF sf a
  return (b, liftMSFTrans sf')

-- | Lift innermost monadic actions in a monad stacks (generalisation of
-- 'liftIO').
liftMSFBase :: (Monad m2, MonadBase m1 m2) => MSF m1 a b -> MSF m2 a b
liftMSFBase sf = MSF $ \a -> do
  (b, sf') <- liftBase $ unMSF sf a
  b `seq` return (b, liftMSFBase sf')

-- *** Generic MSF Lifting

-- IPerez: There is an alternative signature for liftMStreamPurer that also
-- works, and makes the code simpler:
--
-- liftMSFPurer :: Monad m => (m1 (b, MSF m1 a b) -> m (b, MSF m1 a b)) -> MSF m1 a b -> MSF m a b
--
-- Then we can express:
--
-- liftMSFTrans = liftMSFPurer lift
-- liftMSFBase  = liftMSFPurer liftBase
--
-- We could also define a strict version of liftMSFPurer as follows:
--
-- liftMStreamPurer' f = liftMSFPurer (f >=> whnfVal)
--   where whnfVal p@(b,_) = b `seq` return p
--
-- and leave liftMSFPurer as a lazy version (by default).

-- | Lifting purer monadic actions (in an arbitrary way)
liftMSFPurer :: (Monad m2, Monad m1) => (forall c . m1 c -> m2 c) -> MSF m1 a b -> MSF m2 a b
liftMSFPurer liftPurer sf = MSF $ \a -> do
  (b, sf') <- liftPurer $ unMSF sf a
  b `seq` return (b, liftMSFPurer liftPurer sf')

-- ** MSFs within monadic actions

-- | Extract MSF from a monadic action.
--
-- Runs a monadic action that produces an MSF on the first iteration/step, and
-- uses that MSF as the main signal function for all inputs (including the
-- first one).
performOnFirstSample :: Monad m => m (MSF m a b) -> MSF m a b
performOnFirstSample sfaction = MSF $ \a -> do
  sf <- sfaction
  unMSF sf a

-- * Delays

-- | Delay a signal by one sample.
iPre :: Monad m
     => a         -- ^ First output
     -> MSF m a a
iPre firsta = MSF $ \a -> return (firsta, delay a)
-- iPre firsta = feedback firsta $ lift swap
--   where swap (a,b) = (b, a)
-- iPre firsta = next firsta identity

-- | See 'iPre'.

-- FIXME: Remove delay from this module. We should try to make this module
-- small, keeping only primitives.
delay :: Monad m => a -> MSF m a a
delay = iPre

-- * Switching

-- | Switching applies one MSF until it produces a 'Just' output, and then
-- "turns on" a continuation and runs it.
--
-- A more advanced and comfortable approach to switching is givin by Exceptions
-- in "Control.Monad.Trans.MSF.Except"
switch :: Monad m => MSF m a (b, Maybe c) -> (c -> MSF m a b) -> MSF m a b
switch sf f = MSF $ \a -> do
  ((b, c), sf') <- unMSF sf a
  return (b, maybe (switch sf' f) f c)

-- * Feedback loops

-- | Well-formed looped connection of an output component as a future input.
feedback :: Monad m => c -> MSF m (a, c) (b, c) -> MSF m a b
feedback c sf = MSF $ \a -> do
  ((b', c'), sf') <- unMSF sf (a, c)
  return (b', feedback c' sf')

-- * Execution/simulation

-- | Apply a monadic stream function to a list.
--
-- Because the result is in a monad, it may be necessary to
-- traverse the whole list to evaluate the value in the results to WHNF.
-- For example, if the monad is the maybe monad, this may not produce anything
-- if the MSF produces Nothing at any point, so the output stream cannot
-- consumed progressively.
--
-- To explore the output progressively, use liftMSF and (>>>), together
-- with some action that consumes/actuates on the output.
--
-- This is called "runSF" in Liu, Cheng, Hudak, "Causal Commutative Arrows and
-- Their Optimization"
embed :: Monad m => MSF m a b -> [a] -> m [b]
embed _  []     = return []
embed sf (a:as) = do
  (b, sf') <- unMSF sf a
  bs       <- embed sf' as
  return (b:bs)

-- | Run an MSF indefinitely passing a unit-carrying input stream.
reactimate :: Monad m => MSF m () () -> m ()
reactimate sf = do
  (_, sf') <- unMSF sf ()
  reactimate sf'

-- | Run an MSF indefinitely passing a unit-carrying input stream.

-- TODO: A more high-level approach to this would be the use of MaybeT in
-- Control.Monad.Trans.MSF.Maybe
reactimateB :: Monad m => MSF m () Bool -> m ()
reactimateB sf = do
  (b, sf') <- unMSF sf ()
  unless b $ reactimateB sf'