diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -4,10 +4,14 @@
 Since `rhine` reexports modules from `dunai`,
 every major version in `dunai` triggers a major version in `rhine`.
 
-## 0.7.1
+## 0.8.0.0
 
 * Documentation improvements
 * Support for GHC 9.0.2
+* Updated to `dunai-0.8`
+* Added functions to pre-/post-compose SNs and Rhines with ClSFs
+* Added flake & stack support on CI.
+  Thank you, Miguel Negrão and Jun Matsuhita!
 
 ## 0.7.0
 
diff --git a/rhine.cabal b/rhine.cabal
--- a/rhine.cabal
+++ b/rhine.cabal
@@ -1,6 +1,6 @@
 name:                rhine
 
-version:             0.7.1
+version:             0.8.0.0
 
 synopsis: Functional Reactive Programming with type-level clocks
 
@@ -46,7 +46,7 @@
 source-repository this
   type:     git
   location: git@github.com:turion/rhine.git
-  tag:      v0.7.1
+  tag:      v0.8.0.0
 
 
 library
@@ -88,7 +88,6 @@
     FRP.Rhine.Schedule.Trans
     FRP.Rhine.SN
     FRP.Rhine.SN.Combinators
-    FRP.Rhine.TimeDomain
     FRP.Rhine.Type
 
   other-modules:
@@ -100,8 +99,8 @@
   -- other-extensions:
 
   -- Other library packages from which modules are imported.
-  build-depends:       base         >= 4.11 && < 4.16
-                     , dunai        >= 0.6
+  build-depends:       base         >= 4.13 && < 4.16
+                     , dunai        >= 0.8
                      , transformers >= 0.5
                      , time         >= 1.8
                      , free         >= 5.1
@@ -111,16 +110,24 @@
                      , random       >= 1.1
                      , MonadRandom  >= 0.5
                      , simple-affine-space
+                     , time-domain
 
   -- Directories containing source files.
   hs-source-dirs:      src
 
-  ghc-options:         -Wall
-                       -Wno-unticked-promoted-constructors
-                       -Wno-type-defaults
+  ghc-options:  -W
+                -Wno-unticked-promoted-constructors
 
+  if flag(dev)
+    ghc-options: -Werror
+
   if impl(ghc >= 8.6)
     default-extensions: NoStarIsType
 
   -- Base language which the package is written in.
   default-language:    Haskell2010
+
+flag dev
+  description: Enable warnings as errors. Active on ci.
+  default: False
+  manual: True
diff --git a/src/FRP/Rhine/ClSF/Core.hs b/src/FRP/Rhine/ClSF/Core.hs
--- a/src/FRP/Rhine/ClSF/Core.hs
+++ b/src/FRP/Rhine/ClSF/Core.hs
@@ -23,7 +23,6 @@
 import Control.Monad.Trans.Reader (ReaderT, mapReaderT, withReaderT)
 
 -- dunai
-import Data.MonadicStreamFunction (MSF, arrM, constM, morphS, liftTransS)
 import Data.MonadicStreamFunction as X hiding ((>>>^), (^>>>))
 
 -- rhine
diff --git a/src/FRP/Rhine/ClSF/Except.hs b/src/FRP/Rhine/ClSF/Except.hs
--- a/src/FRP/Rhine/ClSF/Except.hs
+++ b/src/FRP/Rhine/ClSF/Except.hs
@@ -12,7 +12,7 @@
 module FRP.Rhine.ClSF.Except
   ( module FRP.Rhine.ClSF.Except
   , module X
-  , safe, safely, Empty, exceptS, runMSFExcept, currentInput
+  , safe, safely, exceptS, runMSFExcept, currentInput
   )
   where
 
diff --git a/src/FRP/Rhine/ClSF/Random.hs b/src/FRP/Rhine/ClSF/Random.hs
--- a/src/FRP/Rhine/ClSF/Random.hs
+++ b/src/FRP/Rhine/ClSF/Random.hs
@@ -15,9 +15,6 @@
 -- transformers
 import Control.Monad.IO.Class
 
--- random
-import System.Random (newStdGen)
-
 -- MonadRandom
 import Control.Monad.Random
 
diff --git a/src/FRP/Rhine/ClSF/Upsample.hs b/src/FRP/Rhine/ClSF/Upsample.hs
--- a/src/FRP/Rhine/ClSF/Upsample.hs
+++ b/src/FRP/Rhine/ClSF/Upsample.hs
@@ -6,12 +6,8 @@
 
 module FRP.Rhine.ClSF.Upsample where
 
--- base
-import Data.Semigroup
-
 -- dunai
 import Control.Monad.Trans.MSF.Reader
---import Data.MonadicStreamFunction
 
 -- rhine
 import FRP.Rhine.ClSF.Core
diff --git a/src/FRP/Rhine/ClSF/Util.hs b/src/FRP/Rhine/ClSF/Util.hs
--- a/src/FRP/Rhine/ClSF/Util.hs
+++ b/src/FRP/Rhine/ClSF/Util.hs
@@ -29,7 +29,6 @@
 
 -- dunai
 import Control.Monad.Trans.MSF.Reader (readerS)
-import Data.MonadicStreamFunction (constM, sumFrom, iPre, feedback)
 import Data.MonadicStreamFunction.Instances.VectorSpace ()
 
 -- simple-affine-space
diff --git a/src/FRP/Rhine/Clock.hs b/src/FRP/Rhine/Clock.hs
--- a/src/FRP/Rhine/Clock.hs
+++ b/src/FRP/Rhine/Clock.hs
@@ -31,8 +31,8 @@
 -- dunai
 import Data.MonadicStreamFunction as X hiding ((>>>^), (^>>>))
 
--- rhine
-import FRP.Rhine.TimeDomain as X
+-- time-domain
+import Data.TimeDomain as X
 
 -- * The 'Clock' type class
 
diff --git a/src/FRP/Rhine/Clock/Periodic.hs b/src/FRP/Rhine/Clock/Periodic.hs
--- a/src/FRP/Rhine/Clock/Periodic.hs
+++ b/src/FRP/Rhine/Clock/Periodic.hs
@@ -17,7 +17,6 @@
 module FRP.Rhine.Clock.Periodic (Periodic (Periodic)) where
 
 -- base
-import Control.Monad (forever)
 import Data.List.NonEmpty hiding (unfold)
 import Data.Maybe (fromMaybe)
 import GHC.TypeLits (Nat, KnownNat, natVal)
diff --git a/src/FRP/Rhine/Clock/Proxy.hs b/src/FRP/Rhine/Clock/Proxy.hs
--- a/src/FRP/Rhine/Clock/Proxy.hs
+++ b/src/FRP/Rhine/Clock/Proxy.hs
@@ -28,12 +28,12 @@
 
 inProxy :: ClockProxy cl -> ClockProxy (In cl)
 inProxy LeafProxy = LeafProxy
-inProxy (SequentialProxy p1 p2) = inProxy p1
+inProxy (SequentialProxy p1 _) = inProxy p1
 inProxy (ParallelProxy pL pR) = ParallelProxy (inProxy pL) (inProxy pR)
 
 outProxy :: ClockProxy cl -> ClockProxy (Out cl)
 outProxy LeafProxy = LeafProxy
-outProxy (SequentialProxy p1 p2) = outProxy p2
+outProxy (SequentialProxy _ p2) = outProxy p2
 outProxy (ParallelProxy pL pR) = ParallelProxy (outProxy pL) (outProxy pR)
 
 -- | Return the incoming tag, assuming that the incoming clock is ticked,
diff --git a/src/FRP/Rhine/Clock/Realtime/Event.hs b/src/FRP/Rhine/Clock/Realtime/Event.hs
--- a/src/FRP/Rhine/Clock/Realtime/Event.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Event.hs
@@ -33,7 +33,6 @@
 -- base
 import Control.Concurrent.Chan
 import Data.Time.Clock
-import Data.Semigroup
 
 -- deepseq
 import Control.DeepSeq
diff --git a/src/FRP/Rhine/Clock/Realtime/Stdin.hs b/src/FRP/Rhine/Clock/Realtime/Stdin.hs
--- a/src/FRP/Rhine/Clock/Realtime/Stdin.hs
+++ b/src/FRP/Rhine/Clock/Realtime/Stdin.hs
@@ -9,9 +9,8 @@
 {-# LANGUAGE TypeFamilies #-}
 module FRP.Rhine.Clock.Realtime.Stdin where
 
--- base
+-- time
 import Data.Time.Clock
-import Data.Semigroup
 
 -- transformers
 import Control.Monad.IO.Class
@@ -19,7 +18,6 @@
 -- rhine
 import FRP.Rhine.Clock
 import FRP.Rhine.Clock.Proxy
-import Data.Semigroup
 
 {- |
 A clock that ticks for every line entered on the console,
diff --git a/src/FRP/Rhine/Clock/Select.hs b/src/FRP/Rhine/Clock/Select.hs
--- a/src/FRP/Rhine/Clock/Select.hs
+++ b/src/FRP/Rhine/Clock/Select.hs
@@ -24,7 +24,6 @@
 
 -- base
 import Data.Maybe (catMaybes, maybeToList)
-import Data.Semigroup
 
 -- | A clock that selects certain subevents of type 'a',
 --   from the tag of a main clock.
@@ -38,6 +37,18 @@
   --   or 'Just a' if the subclock should tick, with tag 'a'.
   , select    :: Tag cl -> Maybe a
   }
+
+instance (Semigroup a, Semigroup cl) => Semigroup (SelectClock cl a) where
+  cl1 <> cl2 = SelectClock
+    { mainClock = mainClock cl1 <> mainClock cl2
+    , select = \tag -> select cl1 tag <> select cl2 tag
+    }
+
+instance (Monoid cl, Semigroup a) => Monoid (SelectClock cl a) where
+  mempty = SelectClock
+    { mainClock = mempty
+    , select = const mempty
+    }
 
 
 instance (Monad m, Clock m cl) => Clock m (SelectClock cl a) where
diff --git a/src/FRP/Rhine/Clock/Util.hs b/src/FRP/Rhine/Clock/Util.hs
--- a/src/FRP/Rhine/Clock/Util.hs
+++ b/src/FRP/Rhine/Clock/Util.hs
@@ -2,10 +2,12 @@
 {-# LANGUAGE RecordWildCards #-}
 module FRP.Rhine.Clock.Util where
 
+-- time-domain
+import Data.TimeDomain
+
 -- rhine
 import FRP.Rhine.Clock
 import FRP.Rhine.Clock.Proxy
-import FRP.Rhine.TimeDomain
 
 -- * Auxiliary definitions and utilities
 
diff --git a/src/FRP/Rhine/Reactimation.hs b/src/FRP/Rhine/Reactimation.hs
--- a/src/FRP/Rhine/Reactimation.hs
+++ b/src/FRP/Rhine/Reactimation.hs
@@ -8,10 +8,6 @@
 {-# LANGUAGE RecordWildCards #-}
 module FRP.Rhine.Reactimation where
 
--- base
-import Control.Monad ((>=>))
-import Data.Functor (void)
-
 -- dunai
 import Data.MonadicStreamFunction.InternalCore
 
@@ -19,12 +15,9 @@
 import FRP.Rhine.Clock
 import FRP.Rhine.Clock.Proxy
 import FRP.Rhine.ClSF.Core
-import FRP.Rhine.Reactimation.ClockErasure
 import FRP.Rhine.Reactimation.Combinators
 import FRP.Rhine.Schedule
 import FRP.Rhine.Type
-
-
 
 -- * Running a Rhine
 
diff --git a/src/FRP/Rhine/Reactimation/ClockErasure.hs b/src/FRP/Rhine/Reactimation/ClockErasure.hs
--- a/src/FRP/Rhine/Reactimation/ClockErasure.hs
+++ b/src/FRP/Rhine/Reactimation/ClockErasure.hs
@@ -13,12 +13,10 @@
 
 -- base
 import Control.Monad (join)
-import Data.Maybe (fromJust, fromMaybe)
 
 -- dunai
 import Control.Monad.Trans.MSF.Reader
 import Data.MonadicStreamFunction
-import Data.MonadicStreamFunction.InternalCore
 
 -- rhine
 import FRP.Rhine.Clock
@@ -26,7 +24,6 @@
 import FRP.Rhine.Clock.Util
 import FRP.Rhine.ClSF hiding (runReaderS)
 import FRP.Rhine.ResamplingBuffer
-import FRP.Rhine.Schedule
 import FRP.Rhine.SN
 
 -- | Run a clocked signal function as a monadic stream function,
@@ -86,6 +83,20 @@
   case tag of
     Left  tagL -> eraseClockSN initialTime snL -< (time, tagL, maybeA)
     Right tagR -> eraseClockSN initialTime snR -< (time, tagR, maybeA)
+
+eraseClockSN initialTime (Postcompose sn clsf) =
+  let
+    proxy = toClockProxy sn
+  in proc input@(time, tag, _) -> do
+  bMaybe <- eraseClockSN initialTime sn -< input
+  mapMaybeS $ eraseClockClSF (outProxy proxy) initialTime clsf -< (time, , ) <$> outTag proxy tag <*> bMaybe
+
+eraseClockSN initialTime (Precompose clsf sn) =
+  let
+    proxy = toClockProxy sn
+  in proc (time, tag, aMaybe) -> do
+  bMaybe <- mapMaybeS $ eraseClockClSF (inProxy proxy) initialTime clsf -< (time, , ) <$> inTag proxy tag <*> aMaybe
+  eraseClockSN initialTime sn -< (time, tag, bMaybe)
 
 -- | Translate a resampling buffer into a monadic stream function.
 --
diff --git a/src/FRP/Rhine/Reactimation/Combinators.hs b/src/FRP/Rhine/Reactimation/Combinators.hs
--- a/src/FRP/Rhine/Reactimation/Combinators.hs
+++ b/src/FRP/Rhine/Reactimation/Combinators.hs
@@ -191,3 +191,40 @@
        -> Rhine m (ParallelClock   m clL clR) a b
 RhineParallelAndSchedule (Rhine sn1 clL) schedule @|| (Rhine sn2 clR)
   = Rhine (sn1 |||| sn2) (ParallelClock clL clR schedule)
+
+
+-- | Postcompose a 'Rhine' with a pure function.
+(@>>^)
+  :: Monad m
+  => Rhine m cl a b
+  ->             (b -> c)
+  -> Rhine m cl a      c
+Rhine sn cl @>>^ f = Rhine (sn >>>^ f) cl
+
+-- | Precompose a 'Rhine' with a pure function.
+(^>>@)
+  :: Monad m
+  =>           (a -> b)
+  -> Rhine m cl      b c
+  -> Rhine m cl a      c
+f ^>>@ Rhine sn cl = Rhine (f ^>>> sn) cl
+
+-- | Postcompose a 'Rhine' with a 'ClSF'.
+(@>-^)
+  :: ( Clock m (Out cl)
+     , Time cl ~ Time (Out cl)
+     )
+  => Rhine m      cl  a b
+  -> ClSF  m (Out cl)   b c
+  -> Rhine m      cl  a   c
+Rhine sn cl @>-^ clsf = Rhine (sn >--^ clsf) cl
+
+-- | Precompose a 'Rhine' with a 'ClSF'.
+(^->@)
+  :: ( Clock m (In cl)
+     , Time cl ~ Time (In cl)
+     )
+  => ClSF  m (In cl) a b
+  -> Rhine m     cl    b c
+  -> Rhine m     cl  a   c
+clsf ^->@ Rhine sn cl = Rhine (clsf ^--> sn) cl
diff --git a/src/FRP/Rhine/ResamplingBuffer.hs b/src/FRP/Rhine/ResamplingBuffer.hs
--- a/src/FRP/Rhine/ResamplingBuffer.hs
+++ b/src/FRP/Rhine/ResamplingBuffer.hs
@@ -18,9 +18,6 @@
 -- rhine
 import FRP.Rhine.Clock
 
--- base
-import Control.Arrow (second)
-
 -- A quick note on naming conventions, to whoever cares:
 -- . Call a single clock @cl@.
 -- . Call several clocks @cl1@, @cl2@ etc. in most situations.
diff --git a/src/FRP/Rhine/SN.hs b/src/FRP/Rhine/SN.hs
--- a/src/FRP/Rhine/SN.hs
+++ b/src/FRP/Rhine/SN.hs
@@ -69,6 +69,22 @@
     => SN m                  cl1      a b
     -> SN m                      cl2  a b
     -> SN m (ParallelClock m cl1 cl2) a b
+  -- | A 'ClSF' can always be postcomposed onto an 'SN' if the clocks match on the output.
+  Postcompose
+    :: ( Clock m (Out cl)
+       , Time cl ~ Time (Out cl)
+       )
+    => SN    m      cl  a b
+    -> ClSF  m (Out cl)   b c
+    -> SN    m      cl  a   c
+  -- | A 'ClSF' can always be precomposed onto an 'SN' if the clocks match on the input.
+  Precompose
+    :: ( Clock m (In cl)
+       , Time cl ~ Time (In cl)
+       )
+    => ClSF m (In cl) a b
+    -> SN   m     cl    b c
+    -> SN   m     cl  a   c
 
 instance GetClockProxy cl => ToClockProxy (SN m cl a b) where
   type Cl (SN m cl a b) = cl
diff --git a/src/FRP/Rhine/SN/Combinators.hs b/src/FRP/Rhine/SN/Combinators.hs
--- a/src/FRP/Rhine/SN/Combinators.hs
+++ b/src/FRP/Rhine/SN/Combinators.hs
@@ -24,7 +24,8 @@
 Synchronous clsf      >>>^ f = Synchronous $ clsf >>^ f
 Sequential sn1 rb sn2 >>>^ f = Sequential sn1 rb     $ sn2 >>>^ f
 Parallel   sn1    sn2 >>>^ f = Parallel  (sn1 >>>^ f) (sn2 >>>^ f)
-
+Postcompose sn clsf >>>^ f = Postcompose sn $ clsf >>^ f
+Precompose clsf sn >>>^ f = Precompose clsf $ sn >>>^ f
 
 -- | Precompose a signal network with a pure function.
 (^>>>)
@@ -35,7 +36,28 @@
 f ^>>> Synchronous clsf      = Synchronous $ f ^>> clsf
 f ^>>> Sequential sn1 rb sn2 = Sequential (f ^>>> sn1) rb      sn2
 f ^>>> Parallel   sn1    sn2 = Parallel   (f ^>>> sn1) (f ^>>> sn2)
+f ^>>> Postcompose sn clsf = Postcompose (f ^>>> sn) clsf
+f ^>>> Precompose clsf sn = Precompose (f ^>> clsf) sn
 
+-- | Postcompose a signal network with a 'ClSF'.
+(>--^)
+  :: ( Clock m (Out cl)
+     , Time cl ~ Time (Out cl)
+     )
+  => SN    m      cl  a b
+  -> ClSF  m (Out cl)   b c
+  -> SN    m      cl  a   c
+(>--^) = Postcompose
+
+-- | Precompose a signal network with a 'ClSF'.
+(^-->)
+  :: ( Clock m (In cl)
+     , Time cl ~ Time (In cl)
+     )
+  => ClSF m (In cl) a b
+  -> SN   m     cl    b c
+  -> SN   m     cl  a   c
+(^-->) = Precompose
 
 -- | Compose two signal networks on the same clock in data-parallel.
 --   At one tick of @cl@, both networks are stepped.
diff --git a/src/FRP/Rhine/Schedule.hs b/src/FRP/Rhine/Schedule.hs
--- a/src/FRP/Rhine/Schedule.hs
+++ b/src/FRP/Rhine/Schedule.hs
@@ -21,9 +21,6 @@
 
 module FRP.Rhine.Schedule where
 
--- base
-import Data.Semigroup
-
 -- transformers
 import Control.Monad.Trans.Reader
 
diff --git a/src/FRP/Rhine/TimeDomain.hs b/src/FRP/Rhine/TimeDomain.hs
deleted file mode 100644
--- a/src/FRP/Rhine/TimeDomain.hs
+++ /dev/null
@@ -1,52 +0,0 @@
-{- |
-This module defines the 'TimeDomain' class.
-Its instances model time.
-Several instances such as 'UTCTime', 'Double' and 'Integer' are supplied here.
--}
-
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE TypeFamilies #-}
-module FRP.Rhine.TimeDomain
-  ( module FRP.Rhine.TimeDomain
-  , UTCTime
-  )
-  where
-
--- time
-import Data.Time.Clock (UTCTime, diffUTCTime)
-
--- | A time domain is an affine space representing a notion of time,
---   such as real time, simulated time, steps, or a completely different notion.
-class TimeDomain time where
-  type Diff time
-  diffTime :: time -> time -> Diff time
-
-
-instance TimeDomain UTCTime where
-  type Diff UTCTime = Double
-  diffTime t1 t2 = realToFrac $ diffUTCTime t1 t2
-
-instance TimeDomain Double where
-  type Diff Double = Double
-  diffTime = (-)
-
-instance TimeDomain Float where
-  type Diff Float = Float
-  diffTime = (-)
-
-instance TimeDomain Integer where
-  type Diff Integer = Integer
-  diffTime          = (-)
-
-instance TimeDomain () where
-  type Diff () = ()
-  diffTime _ _ = ()
-
--- | Any 'Num' can be wrapped to form a 'TimeDomain'.
-newtype NumTimeDomain a = NumTimeDomain { fromNumTimeDomain :: a }
-  deriving Num
-
-instance Num a => TimeDomain (NumTimeDomain a) where
-  type Diff (NumTimeDomain a) = NumTimeDomain a
-  diffTime = (-)
