{-----------------------------------------------------------------------------
    reactive-banana
------------------------------------------------------------------------------}
{-# LANGUAGE RecursiveDo #-}
module Reactive.Banana.Prim (
    -- * Synopsis
    -- | This is an internal module, useful if you want to
    -- implemented your own FRP library.
    -- If you just want to use FRP in your project,
    -- have a look at "Reactive.Banana" instead.
    
    -- * Evaluation
    Step, Network, emptyNetwork,
    
    -- * Build FRP networks
    Build, liftIOLater, BuildIO, liftBuild, buildLater, buildLaterReadNow, compile,
    module Control.Monad.IO.Class,
    
    -- * Caching
    module Reactive.Banana.Prim.Cached,
    
    -- * Testing
    interpret, mapAccumM, mapAccumM_, runSpaceProfile,
    
    -- * IO
    newInput, addHandler, readLatch,
    
    -- * Pulse
    Pulse,
    neverP, alwaysP, mapP, Future, tagFuture, unsafeMapIOP, filterJustP, unionWithP,
    
    -- * Latch
    Latch,
    pureL, mapL, applyL, accumL, applyP,
    
    -- * Dynamic event switching
    switchL, executeP, switchP
    
    -- * Notes
    -- $recursion
  ) where


import Control.Monad.IO.Class
import Reactive.Banana.Prim.Cached
import Reactive.Banana.Prim.Combinators
import Reactive.Banana.Prim.Compile
import Reactive.Banana.Prim.IO
import Reactive.Banana.Prim.Plumbing (neverP, alwaysP, liftBuild, buildLater, buildLaterReadNow, liftIOLater)
import Reactive.Banana.Prim.Types

{-----------------------------------------------------------------------------
    Notes
------------------------------------------------------------------------------}
-- Note [Recursion]
{- $recursion

The 'Build' monad is an instance of 'MonadFix' and supports value recursion.
However, it is built on top of the 'IO' monad, so the recursion is
somewhat limited.

The main rule for value recursion in the 'IO' monad is that the action
to be performed must be known in advance. For instance, the following snippet
will not work, because 'putStrLn' cannot complete its action without
inspecting @x@, which is not defined until later.

>   mdo
>       putStrLn x
>       let x = "Hello recursion"

On the other hand, whenever the sequence of 'IO' actions can be known
before inspecting any later arguments, the recursion works.
For instance the snippet

>   mdo
>       p1 <- mapP p2
>       p2 <- neverP
>       return p1

works because 'mapP' does not inspect its argument. In other words,
a call @p1 <- mapP undefined@ would perform the same sequence of 'IO' actions.
(Internally, it essentially calls 'newIORef'.)

With this issue in mind, almost all operations that build 'Latch'
and 'Pulse' values have been carefully implemented to not inspect
their arguments.
In conjunction with the 'Cached' mechanism for observable sharing,
this allows us to build combinators that can be used recursively.
One notable exception is the 'readLatch' function, which must
inspect its argument in order to be able to read its value.

-}

test :: Build (Pulse ())
test = mdo
    p1 <- mapP (const ()) p2
    p2 <- neverP
    return p1

-- Note [LatchStrictness]
{-

Any value that is stored in the graph over a longer
period of time must be stored in WHNF.

This implies that the values in a latch must be forced to WHNF
when storing them. That doesn't have to be immediately
since we are tying a knot, but it definitely has to be done
before  evaluateGraph  is done.

It also implies that reading a value from a latch must
be forced to WHNF before storing it again, so that we don't
carry around the old collection of latch values.
This is particularly relevant for `applyL`.

Conversely, since latches are the only way to store values over time,
this is enough to guarantee that there are no space leaks in this regard.

-}