diff --git a/CHANGES b/CHANGES
--- a/CHANGES
+++ b/CHANGES
@@ -1,10 +1,23 @@
+2.1.0 - 100805
+* reimplemented the parametric variant in a way that doesn't require
+  signals to carry the type of the parameter any more
+* added the ability to extract the global input in the parametric
+  variant, and also to override it (input and embed, resp.)
+* added until to be able to define switchers that can truly drop
+  references to old signals
+* added debug printing capability to the simple and clocked variants
+* made a note about possibly deprecating the delayed variant
+
 2.0.0 - 100718
-* moved experimental branch to the top (version 1 went into legacy status)
-* added the clocked version
+* moved experimental branch to the top (version 1 went into legacy
+  status)
+* added the clocked variant
 
 1.2.3 - 100131
-* added externalMulti to handle events that can fire several times within a superstep
-* added a cache to the noise signal for safety reasons, so it lives in SignalGen now
+* added externalMulti to handle events that can fire several times
+  within a superstep
+* added a cache to the noise signal for safety reasons, so it lives in
+  SignalGen now
 
 1.2.2 - 100115
 * added noise signals and the getRandom primitive (using mersenne-random)
@@ -13,7 +26,7 @@
 * modified the &&@ and ||@ operators to short-circuit
 
 1.2.0 - 091202
-* added the delayed version to the experimental branch
+* added the delayed variant to the experimental branch
 * renamed storeJust to (-->) in the experimental branch
 
 1.1.0 - 091126
@@ -42,8 +55,10 @@
 
 0.2.0 - 090412
 * removed primitives time and stateless
-* removed default delay on stateful combinators and added experimental cycle detection
-* added some non-primitive combinators: delay, edge, comparisons, logic relations
+* removed default delay on stateful combinators and added experimental
+  cycle detection
+* added some non-primitive combinators: delay, edge, comparisons,
+  logic relations
 * added signal instances for various numeric classes
 
 0.1.0 - 090410
diff --git a/FRP/Elerea/Clocked.hs b/FRP/Elerea/Clocked.hs
--- a/FRP/Elerea/Clocked.hs
+++ b/FRP/Elerea/Clocked.hs
@@ -3,7 +3,7 @@
 {-|
 
 This version differs from the simple one in adding associated freeze
-control signals ("clocks") to stateful entities to be able to pause
+control signals (\'clocks\') to stateful entities to be able to pause
 entire subnetworks without having to write all the low-level logic
 explicitly.  The clocks are fixed to signals upon their creation, and
 the 'withClock' function can be used to specify the common clock for
@@ -84,12 +84,14 @@
     , externalMulti
     , delay
     , generator
-    , withClock
     , memo
+    , until
+    , withClock
     , stateful
     , transfer
     , noise
     , getRandom
+    , debug
     ) where
 
 import Control.Applicative
@@ -98,6 +100,7 @@
 import Control.Monad.Fix
 import Data.IORef
 import Data.Maybe
+import Prelude hiding (until)
 import System.Mem.Weak
 import System.Random.Mersenne
 
@@ -252,6 +255,28 @@
 
   addSignal (const sample) (const (sample >> return ())) ref pool
 
+-- | A signal that is true exactly once: the first time the input
+-- signal is true.  Afterwards, it is constantly false, and it holds
+-- no reference to the input signal.  Note that 'until' always follows
+-- the master clock, i.e. the fastest one, therefore it never creates
+-- a long spike of @True@.
+until :: Signal Bool             -- ^ the boolean input signal
+      -> SignalGen (Signal Bool) -- ^ a one-shot signal true only the first time the input is true
+until (S s) = SG $ \pool _ -> do
+  ref <- newIORef (Ready undefined)
+
+  rsmp <- mfix $ \rs -> newIORef $ do
+    x <- s
+    writeIORef ref (Updated undefined x)
+    when x $ writeIORef rs $ do
+      writeIORef ref (Updated undefined False)
+      return False
+    return x
+
+  let sample = join (readIORef rsmp)
+
+  addSignal (const sample) (const (() <$ sample)) ref pool
+
 -- | A signal that can be directly fed through the sink function
 -- returned.  This can be used to attach the network to the outer
 -- world.
@@ -318,6 +343,10 @@
 -- | A random source within the 'SignalGen' monad.
 getRandom :: MTRandom a => SignalGen a
 getRandom = SG (const (const randomIO))
+
+-- | A printing action within the 'SignalGen' monad.
+debug :: String -> SignalGen ()
+debug = SG . const . const . putStrLn
 
 -- The Show instance is only defined for the sake of Num...
 instance Show (Signal a) where
diff --git a/FRP/Elerea/Delayed.hs b/FRP/Elerea/Delayed.hs
--- a/FRP/Elerea/Delayed.hs
+++ b/FRP/Elerea/Delayed.hs
@@ -1,9 +1,15 @@
 {-|
 
-This version differs from the parametric one in introducing autmatic
+Note: this module is likely to be deprecated in the near future,
+because automatic delays are ill-defined, and not very useful in
+practice anyway.  Experience with the library suggests that
+instantaneous loops are relatively easy to avoid.
+
+This version differs from the parametric one in introducing automatic
 delays.  In practice, if a dependency loop involves a 'transfer'
 primitive, it will be resolved during runtime even if transfer
-functions are not delayed by default.
+functions are not delayed by default.  Also, the until construct is
+missing from this module.
 
 The interface of this module differs from the old Elerea in the
 following ways:
diff --git a/FRP/Elerea/Legacy.hs b/FRP/Elerea/Legacy.hs
--- a/FRP/Elerea/Legacy.hs
+++ b/FRP/Elerea/Legacy.hs
@@ -27,13 +27,12 @@
  integral x0 s = transfer x0 (\\dt x x0 -> x0+x*realToFrac dt) s
 @
 
-Head to "FRP.Elerea.Internal" for the implementation details.  To get
-a general idea how to use the library, check out the sources in the
-@elerea-examples@ package.
+Head to "FRP.Elerea.Legacy.Internal" for the implementation details.
+To get a general idea how to use the library, check out the sources in
+the @elerea-examples@ package.
 
-The "FRP.Elerea.Experimental" branch provides a similar interface with
-a rather different underlying structure, which is likely to be more
-efficient.
+The "FRP.Elerea" branch provides a similar interface with a rather
+different underlying structure, which is likely to be more efficient.
 
 -}
 
diff --git a/FRP/Elerea/Param.hs b/FRP/Elerea/Param.hs
--- a/FRP/Elerea/Param.hs
+++ b/FRP/Elerea/Param.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
 {-|
 
 This version differs from the simple one in providing an extra
@@ -26,8 +28,11 @@
   (i.e. their behaviour doesn't depend on the context any more);
 
 * the user needs to cache the results of applicative operations to be
-  reused in multiple places explicitly using the 'memo' combinator.
+  reused in multiple places explicitly using the 'memo' combinator;
 
+* the input can be retrieved as an explicit signal within the
+  SignalGen monad, and also overridden for parts of the network.
+
 -}
 
 module FRP.Elerea.Param
@@ -37,10 +42,13 @@
     , external
     , externalMulti
     , delay
+    , generator
+    , memo
+    , until
+    , input
+    , embed
     , stateful
     , transfer
-    , memo
-    , generator
     , noise
     , getRandom
     , debug
@@ -52,38 +60,28 @@
 import Control.Monad.Fix
 import Data.IORef
 import Data.Maybe
+import Prelude hiding (until)
 import System.Mem.Weak
 import System.Random.Mersenne
 
 -- | A signal can be thought of as a function of type @Nat -> a@, and
 -- its 'Monad' instance agrees with that intuition.  Internally, is
 -- represented by a sampling computation.
-newtype Signal p a = S { unS :: p -> IO a }
+newtype Signal a = S (IO a) deriving (Functor, Applicative, Monad)
 
 -- | A dynamic set of actions to update a network without breaking
 -- consistency.
-type UpdatePool p = [Weak (p -> IO (), IO ())]
+type UpdatePool = [Weak (IO (), IO ())]
 
 -- | A signal generator is the only source of stateful signals.
 -- Internally, computes a signal structure and adds the new variables
 -- to an existing update pool.
-newtype SignalGen p a = SG { unSG :: IORef (UpdatePool p) -> IO a }
+newtype SignalGen p a = SG { unSG :: IORef UpdatePool -> Signal p -> IO a }
 
 -- | The phases every signal goes through during a superstep: before
 -- or after sampling.
 data Phase s a = Ready s | Aged s a
 
-instance Functor (Signal p) where
-  fmap = liftM
-
-instance Applicative (Signal p) where
-  pure = return
-  (<*>) = ap
-
-instance Monad (Signal p) where
-  return = S . const . return
-  S g >>= f = S $ \p -> g p >>= \x -> unS (f x) p
-
 instance Functor (SignalGen p) where
   fmap = liftM
 
@@ -92,11 +90,11 @@
   (<*>) = ap
 
 instance Monad (SignalGen p) where
-  return = SG . const . return
-  SG g >>= f = SG $ \p -> g p >>= \x -> unSG (f x) p
+  return = SG . const . const . return
+  SG g >>= f = SG $ \p i -> g p i >>= \x -> unSG (f x) p i
 
 instance MonadFix (SignalGen p) where
-  mfix f = SG $ \p -> mfix (($p).unSG.f)
+  mfix f = SG $ \p i -> mfix (($i).($p).unSG.f)
 
 -- | Embedding a signal into an 'IO' environment.  Repeated calls to
 -- the computation returned cause the whole network to be updated, and
@@ -104,48 +102,50 @@
 -- result. The computation accepts a global parameter that will be
 -- distributed to all signals.  For instance, this can be the time
 -- step, if we want to model continuous-time signals.
-start :: SignalGen p (Signal p a) -- ^ the generator of the top-level signal
-      -> IO (p -> IO a)           -- ^ the computation to sample the signal
+start :: SignalGen p (Signal a) -- ^ the generator of the top-level signal
+      -> IO (p -> IO a)         -- ^ the computation to sample the signal
 start (SG gen) = do
   pool <- newIORef []
-  (S sample) <- gen pool
+  (inp,sink) <- external undefined
+  S sample <- gen pool inp
 
   ptrs0 <- readIORef pool
   writeIORef pool []
   (as0,cs0) <- unzip . map fromJust <$> mapM deRefWeak ptrs0
-  let ageStatic param = mapM_ ($param) as0
+  let ageStatic = sequence_ as0
       commitStatic = sequence_ cs0
 
   return $ \param -> do
     let update [] ptrs age commit = do
           writeIORef pool ptrs
-          ageStatic param >> age
+          ageStatic >> age
           commitStatic >> commit
         update (p:ps) ptrs age commit = do
           r <- deRefWeak p
           case r of
             Nothing -> update ps ptrs age commit
-            Just (a,c) -> update ps (p:ptrs) (age >> a param) (commit >> c)
+            Just (a,c) -> update ps (p:ptrs) (age >> a) (commit >> c)
 
-    res <- sample param
+    sink param
+    res <- sample
     ptrs <- readIORef pool
     update ptrs [] (return ()) (return ())
     return res
 
 -- | Auxiliary function used by all the primitives that create a
 -- mutable variable.
-addSignal :: (p -> Phase s a -> IO a)  -- ^ sampling function
-          -> (p -> Phase s a -> IO ()) -- ^ aging function
-          -> IORef (Phase s a)         -- ^ the mutable variable behind the signal
-          -> IORef (UpdatePool p)      -- ^ the pool of update actions
-          -> IO (Signal p a)
+addSignal :: (Phase s a -> IO a)  -- ^ sampling function
+          -> (Phase s a -> IO ()) -- ^ aging function
+          -> IORef (Phase s a)    -- ^ the mutable variable behind the signal
+          -> IORef UpdatePool     -- ^ the pool of update actions
+          -> IO (Signal a)
 addSignal sample age ref pool = do
   let  commit (Aged s _)  = Ready s
        commit _           = error "commit error: signal not aged"
 
-       sig = S $ \p -> readIORef ref >>= sample p
+       sig = S $ readIORef ref >>= sample
 
-  update <- mkWeak sig (\p -> readIORef ref >>= age p, modifyIORef ref commit) Nothing
+  update <- mkWeak sig (readIORef ref >>= age, modifyIORef ref commit) Nothing
   modifyIORef pool (update:)
   return sig
 
@@ -153,63 +153,92 @@
 -- the previous superstep, starting with the filler value given in the
 -- first argument.
 delay :: a                        -- ^ initial output
-      -> Signal p a               -- ^ the signal to delay
-      -> SignalGen p (Signal p a)
-delay x0 (S s) = SG $ \pool -> do
+      -> Signal a                 -- ^ the signal to delay
+      -> SignalGen p (Signal a)
+delay x0 (S s) = SG $ \pool _ -> do
   ref <- newIORef (Ready x0)
 
-  let  sample _ (Ready x)   = return x
-       sample _ (Aged _ x)  = return x
+  let  sample (Ready x)   = return x
+       sample (Aged _ x)  = return x
 
-       age p (Ready x)  = s p >>= \x' -> x' `seq` writeIORef ref (Aged x' x)
-       age _ _          = return ()
+       age (Ready x)  = s >>= \x' -> x' `seq` writeIORef ref (Aged x' x)
+       age _          = return ()
 
   addSignal sample age ref pool
 
 -- | Memoising combinator.  It can be used to cache results of
 -- applicative combinators in case they are used in several
 -- places. Other than that, it is equivalent to 'return'.
-memo :: Signal p a               -- ^ signal to memoise
-     -> SignalGen p (Signal p a)
-memo (S s) = SG $ \pool -> do
+memo :: Signal a               -- ^ signal to memoise
+     -> SignalGen p (Signal a)
+memo (S s) = SG $ \pool _ -> do
   ref <- newIORef (Ready undefined)
 
-  let  sample p (Ready _)      = s p >>= \x -> writeIORef ref (Aged undefined x) >> return x
-       sample _ (Aged _ x)     = return x
+  let  sample (Ready _)      = s >>= \x -> writeIORef ref (Aged undefined x) >> return x
+       sample (Aged _ x)     = return x
 
-       age p (Ready _)      = s p >>= \x -> writeIORef ref (Aged undefined x)
-       age _ _              = return ()
+       age (Ready _)      = s >>= \x -> writeIORef ref (Aged undefined x)
+       age _              = return ()
 
   addSignal sample age ref pool
 
 -- | A reactive signal that takes the value to output from a monad
 -- carried by its input.  It is possible to create new signals in the
 -- monad.
-generator :: Signal p (SignalGen p a) -- ^ a stream of generators to potentially run
-          -> SignalGen p (Signal p a)
-generator (S gen) = SG $ \pool -> do
+generator :: Signal (SignalGen p a) -- ^ a stream of generators to potentially run
+          -> SignalGen p (Signal a)
+generator (S gen) = SG $ \pool inp -> do
   ref <- newIORef (Ready undefined)
 
-  let  next p = ($pool).unSG =<< gen p
+  let  next = ($inp).($pool).unSG =<< gen
 
-       sample p (Ready _)  = next p >>= \x' -> writeIORef ref (Aged x' x') >> return x'
-       sample _ (Aged _ x) = return x
+       sample (Ready _)  = next >>= \x' -> writeIORef ref (Aged x' x') >> return x'
+       sample (Aged _ x) = return x
 
-       age p (Ready _) = next p >>= \x' -> writeIORef ref (Aged x' x')
-       age _ _         = return ()
+       age (Ready _) = next >>= \x' -> writeIORef ref (Aged x' x')
+       age _         = return ()
 
   addSignal sample age ref pool
 
+-- | A signal that is true exactly once: the first time the input
+-- signal is true.  Afterwards, it is constantly false, and it holds
+-- no reference to the input signal.
+until :: Signal Bool               -- ^ the boolean input signal
+      -> SignalGen p (Signal Bool) -- ^ a one-shot signal true only the first time the input is true
+until (S s) = SG $ \pool _ -> do
+  ref <- newIORef (Ready undefined)
+
+  rsmp <- mfix $ \rs -> newIORef $ do
+    x <- s
+    writeIORef ref (Aged undefined x)
+    when x $ writeIORef rs $ do
+      writeIORef ref (Aged undefined False)
+      return False
+    return x
+
+  let sample = join (readIORef rsmp)
+
+  addSignal (const sample) (const (() <$ sample)) ref pool
+
+-- | The common input signal that is fed through the function returned
+-- by 'start', unless we are in an 'embed'ded generator.
+input :: SignalGen p (Signal p)
+input = SG $ const return
+
+-- | Embed a generator with an overridden input signal.
+embed :: Signal p' -> SignalGen p' a -> SignalGen p a
+embed s (SG g) = SG $ \pool _ -> g pool s
+
 -- | A signal that can be directly fed through the sink function
 -- returned.  This can be used to attach the network to the outer
 -- world.  Note that this is optional, as all the input of the network
 -- can be fed in through the global parameter, although that is not
 -- really convenient for many signals.
-external :: a                           -- ^ initial value
-         -> IO (Signal p a, a -> IO ()) -- ^ the signal and an IO function to feed it
+external :: a                         -- ^ initial value
+         -> IO (Signal a, a -> IO ()) -- ^ the signal and an IO function to feed it
 external x = do
   ref <- newIORef x
-  return (S (const (readIORef ref)), writeIORef ref)
+  return (S (readIORef ref), writeIORef ref)
 
 -- | An event-like signal that can be fed through the sink function
 -- returned.  The signal carries a list of values fed in since the
@@ -219,12 +248,12 @@
 -- 'external' this function only returns a generator to be used within
 -- the expression constructing the top-level stream, and this
 -- generator can only be used once.
-externalMulti :: IO (SignalGen p (Signal p [a]), a -> IO ()) -- ^ a generator for the event signal and the associated sink
+externalMulti :: IO (SignalGen p (Signal [a]), a -> IO ()) -- ^ a generator for the event signal and the associated sink
 externalMulti = do
   var <- newMVar []
-  return (SG $ \pool -> do
-             let sig = S $ const (readMVar var)
-             update <- mkWeak sig (const (return ()),takeMVar var >> putMVar var []) Nothing
+  return (SG $ \pool _ -> do
+             let sig = S $ readMVar var
+             update <- mkWeak sig (return (),takeMVar var >> putMVar var []) Nothing
              modifyIORef pool (update:)
              return sig
          ,\val -> do  vals <- takeMVar var
@@ -233,68 +262,64 @@
 
 -- | A pure stateful signal.  The initial state is the first output,
 -- and every following output is calculated from the previous one and
--- the value of the global parameter.
-stateful :: a -> (p -> a -> a) -> SignalGen p (Signal p a)
-stateful x0 f = SG $ \pool -> do
-  ref <- newIORef (Ready x0)
-
-  let  sample _ (Ready x)  = return x
-       sample _ (Aged _ x) = return x
-
-       age p (Ready x) = let x' = f p x in x' `seq` writeIORef ref (Aged x' x)
-       age _ _         = return ()
-
-  addSignal sample age ref pool
+-- the value of the global parameter (which might have been overridden
+-- by 'embed').  It is equivalent to the following expression:
+--
+-- @
+--  stateful x0 f = 'mfix' $ \sig -> 'input' >>= \i -> 'delay' x0 (f '<$>' i '<*>' sig)
+-- @
+stateful :: a                    -- ^ initial state
+         -> (p -> a -> a)        -- ^ state transformation
+         -> SignalGen p (Signal a)
+stateful x0 f = mfix $ \sig -> input >>= \i -> delay x0 (f <$> i <*> sig)
 
 -- | A stateful transfer function.  The current input affects the
 -- current output, i.e. the initial state given in the first argument
 -- is considered to appear before the first output, and can never be
 -- observed.  Every output is derived from the current value of the
--- input signal, the global parameter and the previous output.
-transfer :: a -> (p -> t -> a -> a) -> Signal p t -> SignalGen p (Signal p a)
-transfer x0 f (S s) = SG $ \pool -> do
-  ref <- newIORef (Ready x0)
-
-  let  sample p (Ready x)  = s p >>= \y -> let x' = f p y x in
-                                            x' `seq` writeIORef ref (Aged x' x') >> return x'
-       sample _ (Aged _ x) = return x
-
-       age p (Ready x) = s p >>= \y -> let x' = f p y x in
-                                        x' `seq` writeIORef ref (Aged x' x')
-       age _ _         = return ()
-
-  addSignal sample age ref pool
+-- input signal, the global parameter (which might have been
+-- overridden by 'embed') and the previous output.  It is equivalent
+-- to the following expression:
+--
+-- @
+--  transfer x0 f s = 'mfix' $ \sig -> 'input' >>= \i -> 'liftA3' f i s '<$>' 'delay' x0 sig
+-- @
+transfer :: a                    -- ^ initial internal state
+         -> (p -> t -> a -> a)   -- ^ state updater function
+         -> Signal t             -- ^ input signal
+         -> SignalGen p (Signal a)
+transfer x0 f s = mfix $ \sig -> input >>= \i -> liftA3 f i s <$> delay x0 sig
 
 -- | A random signal.
-noise :: MTRandom a => SignalGen p (Signal p a)
-noise = memo (S (const randomIO))
+noise :: MTRandom a => SignalGen p (Signal a)
+noise = memo (S randomIO)
 
 -- | A random source within the 'SignalGen' monad.
 getRandom :: MTRandom a => SignalGen p a
-getRandom = SG (const randomIO)
+getRandom = SG (const (const randomIO))
 
 -- | A printing action within the 'SignalGen' monad.
 debug :: String -> SignalGen p ()
-debug = SG . const . putStrLn
+debug = SG . const . const . putStrLn
 
 -- | The @Show@ instance is only defined for the sake of 'Num'...
-instance Show (Signal p a) where
+instance Show (Signal a) where
   showsPrec _ _ s = "<SIGNAL>" ++ s
 
 -- | Equality test is impossible.
-instance Eq (Signal p a) where
+instance Eq (Signal a) where
   _ == _ = False
 
 -- | Error message for unimplemented instance functions.
 unimp :: String -> a
 unimp = error . ("Signal: "++)
 
-instance Ord t => Ord (Signal p t) where
+instance Ord t => Ord (Signal t) where
   compare = unimp "compare"
   min = liftA2 min
   max = liftA2 max
 
-instance Enum t => Enum (Signal p t) where
+instance Enum t => Enum (Signal t) where
   succ = fmap succ
   pred = fmap pred
   toEnum = pure . toEnum
@@ -304,11 +329,11 @@
   enumFromTo = unimp "enumFromTo"
   enumFromThenTo = unimp "enumFromThenTo"
 
-instance Bounded t => Bounded (Signal p t) where
+instance Bounded t => Bounded (Signal t) where
   minBound = pure minBound
   maxBound = pure maxBound
 
-instance Num t => Num (Signal p t) where
+instance Num t => Num (Signal t) where
   (+) = liftA2 (+)
   (-) = liftA2 (-)
   (*) = liftA2 (*)
@@ -317,10 +342,10 @@
   negate = fmap negate
   fromInteger = pure . fromInteger
 
-instance Real t => Real (Signal p t) where
+instance Real t => Real (Signal t) where
   toRational = unimp "toRational"
 
-instance Integral t => Integral (Signal p t) where
+instance Integral t => Integral (Signal t) where
   quot = liftA2 quot
   rem = liftA2 rem
   div = liftA2 div
@@ -331,12 +356,12 @@
     where dmab = divMod <$> a <*> b
   toInteger = unimp "toInteger"
 
-instance Fractional t => Fractional (Signal p t) where
+instance Fractional t => Fractional (Signal t) where
   (/) = liftA2 (/)
   recip = fmap recip
   fromRational = pure . fromRational
 
-instance Floating t => Floating (Signal p t) where
+instance Floating t => Floating (Signal t) where
   pi = pure pi
   exp = fmap exp
   sqrt = fmap sqrt
diff --git a/FRP/Elerea/Simple.hs b/FRP/Elerea/Simple.hs
--- a/FRP/Elerea/Simple.hs
+++ b/FRP/Elerea/Simple.hs
@@ -131,10 +131,12 @@
     , delay
     , generator
     , memo
+    , until
     , stateful
     , transfer
     , noise
     , getRandom
+    , debug
     ) where
 
 import Control.Applicative
@@ -143,6 +145,7 @@
 import Control.Monad.Fix
 import Data.IORef
 import Data.Maybe
+import Prelude hiding (until)
 import System.Mem.Weak
 import System.Random.Mersenne
 
@@ -273,7 +276,7 @@
                    writeIORef ref (Updated undefined x)
                    return x
 
-  addSignal (const sample) (const (sample >> return ())) ref pool
+  addSignal (const sample) (const (() <$ sample)) ref pool
 
 -- | Memoising combinator.  It can be used to cache results of
 -- applicative combinators in case they are used in several places.
@@ -286,8 +289,36 @@
 
   let sample = s >>= \x -> writeIORef ref (Updated undefined x) >> return x
 
-  addSignal (const sample) (const (sample >> return ())) ref pool
+  addSignal (const sample) (const (() <$ sample)) ref pool
 
+-- | A signal that is true exactly once: the first time the input
+-- signal is true.  Afterwards, it is constantly false, and it holds
+-- no reference to the input signal.  It is observationally equivalent
+-- to the following expression (which would hold onto @s@ forever):
+--
+-- @
+--  until s = do
+--    step <- 'transfer' False (||) s
+--    dstep <- 'delay' False step
+--    return $ 'liftA2' (/=) step dstep
+-- @
+until :: Signal Bool             -- ^ the boolean input signal
+      -> SignalGen (Signal Bool) -- ^ a one-shot signal true only the first time the input is true
+until (S s) = SG $ \pool -> do
+  ref <- newIORef (Ready undefined)
+
+  rsmp <- mfix $ \rs -> newIORef $ do
+    x <- s
+    writeIORef ref (Updated undefined x)
+    when x $ writeIORef rs $ do
+      writeIORef ref (Updated undefined False)
+      return False
+    return x
+
+  let sample = join (readIORef rsmp)
+
+  addSignal (const sample) (const (() <$ sample)) ref pool
+
 -- | A signal that can be directly fed through the sink function
 -- returned.  This can be used to attach the network to the outer
 -- world.
@@ -354,6 +385,10 @@
 -- | A random source within the 'SignalGen' monad.
 getRandom :: MTRandom a => SignalGen a
 getRandom = SG (const randomIO)
+
+-- | A printing action within the 'SignalGen' monad.
+debug :: String -> SignalGen ()
+debug = SG . const . putStrLn
 
 -- The Show instance is only defined for the sake of Num...
 instance Show (Signal a) where
diff --git a/elerea.cabal b/elerea.cabal
--- a/elerea.cabal
+++ b/elerea.cabal
@@ -1,11 +1,11 @@
 Name:                elerea
-Version:             2.0.0
+Version:             2.1.0
 Cabal-Version:       >= 1.2
 Synopsis:            A minimalistic FRP library
 Category:            reactivity, FRP
 Description:
 
- Elerea (Eventless reactivity) is a tiny continuous-time FRP
+ Elerea (Eventless reactivity) is a tiny discrete time FRP
  implementation without the notion of event-based switching and
  sampling, with first-class signals (time-varying values).  Reactivity
  is provided through various higher-order constructs that also allow
@@ -15,15 +15,28 @@
  Stateful signals can be safely generated at any time through a
  specialised monad, while stateless combinators can be used in a
  purely applicative style.  Elerea signals can be defined recursively,
- and external input is trivial to attach.  A unique feature of the
- library is that cyclic dependencies are detected on the fly and
- resolved by inserting delays dynamically, unless the user does it
- explicitly.
+ and external input is trivial to attach.  The library comes in four
+ major variants:
  .
+ * Simple: signals are plain discrete streams isomorphic to functions
+   over natural numbers;
+ .
+ * Param: adds a globally accessible input signal for convenience;
+ .
+ * Clocked: adds the ability to freeze whole subnetworks at will;
+ .
+ * Delayed: attempts to resolve instantaneous dependency cycles
+   (i.e. cycles without a delay); this variant is likely to be
+   deprecated in the near future.
+ .
+ The first three variants come with precise denotational semantics.
+ .
  This is a minimal library that defines only some basic primitives,
  and you are advised to install @elerea-examples@ as well to get an
  idea how to build non-trivial systems with it.  The examples are
  separated in order to minimise the dependencies of the core library.
+ The @dow@ package contains a full game built on top of the simple
+ variant.
 
 Author:              Patai Gergely
 Maintainer:          Patai Gergely (patai@iit.bme.hu)
