diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,50 @@
 # TidalCycles log of changes
 
+## 1.9.4 - Stitch
+
+### What's Changed
+* fix #986 by @jwaldmann in https://github.com/tidalcycles/Tidal/pull/987
+* fix #988 by @jwaldmann in https://github.com/tidalcycles/Tidal/pull/989
+* Tidal listener update and rework by @polymorphicengine in https://github.com/tidalcycles/Tidal/pull/996
+* ci: update GitHub actions by @SpencerIsGiddy in https://github.com/tidalcycles/Tidal/pull/994
+* Fix markup errors in and add to Sound.Tidal.UI documentation. by @patrickt in https://github.com/tidalcycles/Tidal/pull/992
+* reset and restart by @yaxu in https://github.com/tidalcycles/Tidal/pull/997
+* Add extractN and extend getN by @thgrund in https://github.com/tidalcycles/Tidal/pull/999
+
+### New Contributors
+* @SpencerIsGiddy made their first contribution in https://github.com/tidalcycles/Tidal/pull/994
+* @patrickt made their first contribution in https://github.com/tidalcycles/Tidal/pull/992
+
+**Full Changelog**: https://github.com/tidalcycles/Tidal/compare/v1.9.3...v1.9.4
+
+### 1.9.3 - Kolam
+
+### What's Changed
+* fix for squeezejoin  by @yaxu in https://github.com/tidalcycles/Tidal/pull/950
+* tidal.el fix silencer by @zzkt in https://github.com/tidalcycles/Tidal/pull/955
+* add accelerate to nobus and fix typo for begin by @yaxu in https://github.com/tidalcycles/Tidal/pull/956
+* Fix warnings by @Zalastax in https://github.com/tidalcycles/Tidal/pull/962
+* Add a Nix Flake for building and developing Tidal by @mitchmindtree in https://github.com/tidalcycles/Tidal/pull/963
+* Make distrib only take structure from the euclid by @yaxu in https://github.com/tidalcycles/Tidal/pull/961
+* Fix build by @yaxu in https://github.com/tidalcycles/Tidal/pull/965
+* Fix resetCycles skipping and lateness by @Zalastax in https://github.com/tidalcycles/Tidal/pull/966
+* Add setCycle by @Zalastax in https://github.com/tidalcycles/Tidal/pull/969
+* CHANGELOG gardening by @mindofmatthew in https://github.com/tidalcycles/Tidal/pull/971
+* Comments for some state functions by @JeffreyBenjaminBrown in https://github.com/tidalcycles/Tidal/pull/973
+* Comments on time, and haddock fixes by @JeffreyBenjaminBrown in https://github.com/tidalcycles/Tidal/pull/974
+* Fix warning about type constraint by @mindofmatthew in https://github.com/tidalcycles/Tidal/pull/977
+* Fix squeeze join by @yaxu in https://github.com/tidalcycles/Tidal/pull/975
+* `trigger` and friends without pattern IDs by @mindofmatthew in https://github.com/tidalcycles/Tidal/pull/970
+* Rename ghost'' to ghostWith by @Zalastax in https://github.com/tidalcycles/Tidal/pull/967
+* Added minor major seventh chord by @jqueraltmo in https://github.com/tidalcycles/Tidal/pull/978
+* support for hosc-0.20 by @yaxu in https://github.com/tidalcycles/Tidal/pull/981
+
+### New Contributors
+* @mitchmindtree made their first contribution in https://github.com/tidalcycles/Tidal/pull/963
+* @jqueraltmo made their first contribution in https://github.com/tidalcycles/Tidal/pull/978
+
+**Full Changelog**: https://github.com/tidalcycles/Tidal/compare/v1.9.2...v1.9.3
+
 ## 1.9.2 - Siteswap c
 Same with 1.9.1, but now with new tidal-link version 1.0.1
 
diff --git a/src/Sound/Tidal/Pattern.hs b/src/Sound/Tidal/Pattern.hs
--- a/src/Sound/Tidal/Pattern.hs
+++ b/src/Sound/Tidal/Pattern.hs
@@ -235,6 +235,40 @@
              p' <- subArc oPart iPart
              return (Event (combineContexts [iContext, oContext]) w' p' v)
 
+
+_trigJoin :: Bool -> Pattern (Pattern a) -> Pattern a
+_trigJoin cycleZero pat_of_pats = Pattern q
+  where q st =
+          catMaybes $
+          concatMap
+          (\oe@(Event oc (Just jow) op ov) ->
+             map (\oe@(Event ic (iw) ip iv) ->
+                    do w <- subMaybeArc (Just jow) iw
+                       p <- subArc op ip
+                       return $ Event (combineContexts [ic, oc]) w p iv
+                 )
+               $ query (((if cycleZero then id else cyclePos) $ start jow) `rotR` ov) st
+          )
+          (query (filterDigital pat_of_pats) st)
+
+trigJoin :: Pattern (Pattern a) -> Pattern a
+trigJoin = _trigJoin False
+
+trigZeroJoin :: Pattern (Pattern a) -> Pattern a
+trigZeroJoin = _trigJoin True
+
+reset :: Pattern Bool -> Pattern a -> Pattern a
+reset bp pat = trigJoin $ (\v -> if v then pat else silence) <$> bp
+
+resetTo :: Pattern Rational -> Pattern a -> Pattern a
+resetTo bp pat = trigJoin $ (\v -> rotL v pat) <$> bp
+
+restart :: Pattern Bool -> Pattern a -> Pattern a
+restart bp pat = trigZeroJoin $ (\v -> if v then pat else silence) <$> bp
+
+restartTo :: Pattern Rational -> Pattern a -> Pattern a
+restartTo bp pat = trigZeroJoin $ (\v -> rotL v pat) <$> bp
+
 -- | * Patterns as numbers
 
 noOv :: String -> a
@@ -453,6 +487,10 @@
 extractR :: String -> ControlPattern -> Pattern Rational
 extractR = _extract getR
 
+-- | Extract a pattern of note values by from a control pattern, given the name of the control
+extractN :: String -> ControlPattern -> Pattern Note 
+extractN = _extract getN
+
 compressArc :: Arc -> Pattern a -> Pattern a
 compressArc (Arc s e) p | s > e = empty
                         | s > 1 || e > 1 = empty
@@ -896,6 +934,7 @@
 getF _  = Nothing
 
 getN :: Value -> Maybe Note
+getN (VN n) = Just n
 getN (VF f) = Just $ Note f
 getN (VR x) = Just $ Note $ fromRational x
 getN (VI x) = Just $ Note $ fromIntegral x
diff --git a/src/Sound/Tidal/UI.hs b/src/Sound/Tidal/UI.hs
--- a/src/Sound/Tidal/UI.hs
+++ b/src/Sound/Tidal/UI.hs
@@ -1,7 +1,5 @@
 {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings #-}
 
-module Sound.Tidal.UI where
-
 {-
     UI.hs - Tidal's main 'user interface' functions, for transforming
     patterns, building on the Core ones.
@@ -21,6 +19,20 @@
     along with this library.  If not, see <http://www.gnu.org/licenses/>.
 -}
 
+{-|
+    This module provides the main user interface functions, including sources
+    of randomness and transformations of patterns. All these functions are available
+    in the context of the TidalCycles REPL.
+
+    Many functions in this module taking 'Pattern' values as arguments have a
+    corresponding function with an underscore prepended to its name (e.g.
+    'degradeBy' and '_degradeBy'). These functions accept plain values, not
+    'Pattern's, and are generally intended for those developing or extending Tidal.
+
+-}
+
+module Sound.Tidal.UI where
+
 import           Prelude hiding ((<*), (*>))
 
 import           Data.Char (digitToInt, isDigit, ord)
@@ -43,10 +55,19 @@
 ------------------------------------------------------------------------
 -- * UI
 
--- | Randomisation
+-- ** Randomisation
 
--- cf. George Marsaglia (2003). "Xorshift RNGs". Journal of Statistical Software 8:14.
--- https://www.jstatsoft.org/article/view/v008i14
+
+{-|
+An implementation of the well-known @xorshift@ random number generator.
+Given a seed number, generates a reasonably random number out of it.
+This is an efficient algorithm suitable for use in tight loops and used
+to implement the below functions, which are used to implement 'rand'.
+
+See George Marsaglia (2003). ["Xorshift RNGs"](@https://www.jstatsoft.org/article/view/v008i14@),
+in Journal of Statistical Software, pages 8–14.
+
+-}
 xorwise :: Int -> Int
 xorwise x =
   let a = xor (shiftL x 13) x
@@ -73,24 +94,23 @@
 
 {-|
 
-`rand` generates a continuous pattern of (pseudo-)random numbers between `0` and `1`.
+`rand` generates a continuous pattern of (pseudo-)random numbers between @0@ and @1@.
 
 @
 sound "bd*8" # pan rand
 @
 
-pans bass drums randomly
+pans bass drums randomly, and
 
 @
 sound "sn sn ~ sn" # gain rand
 @
 
-makes the snares' randomly loud and quiet.
+makes the snares randomly loud and quiet.
 
-Numbers coming from this pattern are 'seeded' by time. So if you reset
-time (via `cps (-1)`, then `cps 1.1` or whatever cps you want to
-restart with) the random pattern will emit the exact same _random_
-numbers again.
+Numbers coming from this pattern are \'seeded\' by time. So if you reset time
+(using 'resetCycles', 'setCycle', or 'cps') the random pattern will emit the
+exact same _random_ numbers again.
 
 In cases where you need two different random patterns, you can shift
 one of them around to change the time from which the _random_ pattern
@@ -109,18 +129,18 @@
 rand :: Fractional a => Pattern a
 rand = Pattern (\(State a@(Arc s e) _) -> [Event (Context []) Nothing a (realToFrac $ (timeToRand ((e + s)/2) :: Double))])
 
--- | Boolean rand - a continuous stream of true/false values, with a 50/50 chance.
+-- | Boolean rand - a continuous stream of true\/false values, with a 50\/50 chance.
 brand :: Pattern Bool
 brand = _brandBy 0.5
 
--- | Boolean rand with probability as input, e.g. brandBy 0.25 is 25% chance of being true.
+-- | Boolean rand with probability as input, e.g. @brandBy 0.25@ produces trues 25% of the time.
 brandBy :: Pattern Double -> Pattern Bool
 brandBy probpat = innerJoin $ (\prob -> _brandBy prob) <$> probpat
 
 _brandBy :: Double -> Pattern Bool
 _brandBy prob = fmap (< prob) rand
 
-{- | Just like `rand` but for whole numbers, `irand n` generates a pattern of (pseudo-) random whole numbers between `0` to `n-1` inclusive. Notably used to pick a random
+{- | Just like `rand` but for whole numbers, @irand n@ generates a pattern of (pseudo-) random whole numbers between @0@ to @n-1@ inclusive. Notably used to pick a random
 samples from a folder:
 
 @
@@ -150,11 +170,13 @@
   interp x a b = a + smootherStep x * (b-a)
   smootherStep x = 6.0 * x**5 - 15.0 * x**4 + 10.0 * x**3
 
+-- | As 'perlin' with a suitable choice of input pattern (@'sig' 'fromRational'@).
 perlin :: Fractional a => Pattern a
 perlin = perlinWith (sig fromRational)
 
-{- `perlin2With` is Perlin noise with a 2-dimensional input. This can be
+{-| `perlin2With` is Perlin noise with a 2-dimensional input. This can be
 useful for more control over how the randomness repeats (or doesn't).
+
 @
 d1
  $ s "[supersaw:-12*32]"
@@ -183,6 +205,7 @@
                           + (1.0 - s x') * s y' * c  +  s x' * s y' * d
   s x' = 6.0 * x'**5 - 15.0 * x'**4 + 10.0 * x'**3
 
+-- | As 'perlin2' with a suitable choice of input pattern (@'sig' 'fromRational'@).
 perlin2 :: Pattern Double -> Pattern Double
 perlin2 = perlin2With (sig fromRational)
 
@@ -197,6 +220,13 @@
 choose :: [a] -> Pattern a
 choose = chooseBy rand
 
+
+{- | Given a pattern of doubles, 'chooseBy' normalizes them so that each
+corresponds to an index in the provided list. The returned pattern
+contains the corresponding elements in the list.
+
+@'choose' = chooseBy 'rand'@
+-}
 chooseBy :: Pattern Double -> [a] -> Pattern a
 chooseBy _ [] = silence
 chooseBy f xs = (xs !!!) . floor <$> range 0 (fromIntegral $ length xs) f
@@ -214,6 +244,13 @@
 wchoose :: [(a,Double)] -> Pattern a
 wchoose = wchooseBy rand
 
+{- | Given a pattern of probabilities and an list of @(value, weight)@ pairs,
+'wchooseBy' creates a @'Pattern' value@ by choosing values based on those
+probabilities and, weighted appropriately by the weights in the list of pairs.
+
+@'wchoose' = wchooseBy 'rand'@
+
+-}
 wchooseBy :: Pattern Double -> [(a,Double)] -> Pattern a
 wchooseBy pat pairs = match <$> pat
   where
@@ -222,8 +259,41 @@
     values = map fst pairs
     total = sum $ map snd pairs
 
+-- | @randcat ps@: does a @slowcat@ on the list of patterns @ps@ but
+-- randomises the order in which they are played.
+randcat :: [Pattern a] -> Pattern a
+randcat ps = spread' rotL (_segment 1 $ (% 1) . fromIntegral <$> (_irand (length ps) :: Pattern Int)) (slowcat ps)
+
+-- | As 'randcat', but allowing weighted choice.
+wrandcat :: [(Pattern a, Double)] -> Pattern a
+wrandcat ps = unwrap $ wchooseBy (segment 1 rand) ps
+
+{- | `degrade` randomly removes events from a pattern 50% of the time:
+
+@
+d1 $ slow 2 $ degrade $ sound "[[[feel:5*8,feel*3] feel:3*8], feel*4]"
+   # accelerate "-6"
+   # speed "2"
+@
+
+The shorthand syntax for `degrade` is a question mark: `?`. Using `?`
+will allow you to randomly remove events from a portion of a pattern:
+
+@
+d1 $ slow 2 $ sound "bd ~ sn bd ~ bd? [sn bd?] ~"
+@
+
+You can also use `?` to randomly remove events from entire sub-patterns:
+
+@
+d1 $ slow 2 $ sound "[[[feel:5*8,feel*3] feel:3*8]?, feel*4]"
+@
+-}
+degrade :: Pattern a -> Pattern a
+degrade = _degradeBy 0.5
+
 {- |
-Similar to `degrade` `degradeBy` allows you to control the percentage of events that
+Similar to `degrade`, `degradeBy` allows you to control the percentage of events that
 are removed. For example, to remove events 90% of the time:
 
 @
@@ -232,6 +302,12 @@
    # speed "2"
 @
 
+You can also invoke this behavior in the shorthand notation by specifying a percentage, as a
+number between 0 and 1, after the question mark:
+
+@
+d1 $ s "bd hh?0.8 bd hh?0.4"
+@
 -}
 degradeBy :: Pattern Double -> Pattern a -> Pattern a
 degradeBy = tParam _degradeBy
@@ -243,6 +319,10 @@
 _degradeByUsing :: Pattern Double -> Double -> Pattern a -> Pattern a
 _degradeByUsing prand x p = fmap fst $ filterValues ((> x) . snd) $ (,) <$> p <* prand
 
+{-|
+As 'degradeBy', but the pattern of probabilities represents the chances to retain rather
+than remove the corresponding element.
+-}
 unDegradeBy :: Pattern Double -> Pattern a -> Pattern a
 unDegradeBy = tParam _unDegradeBy
 
@@ -254,7 +334,7 @@
 
 
 {- | Use @sometimesBy@ to apply a given function "sometimes". For example, the
-following code results in `density 2` being applied about 25% of the time:
+following code results in @density 2@ being applied about 25% of the time:
 
 @
 d1 $ sometimesBy 0.25 (density 2) $ sound "bd*8"
@@ -263,62 +343,88 @@
 There are some aliases as well:
 
 @
-sometimes = sometimesBy 0.5
-often = sometimesBy 0.75
-rarely = sometimesBy 0.25
-almostNever = sometimesBy 0.1
-almostAlways = sometimesBy 0.9
+'sometimes' = sometimesBy 0.5
+'often' = sometimesBy 0.75
+'rarely' = sometimesBy 0.25
+'almostNever' = sometimesBy 0.1
+'almostAlways' = sometimesBy 0.9
 @
 -}
 sometimesBy :: Pattern Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 sometimesBy x f pat = overlay (degradeBy x pat) (f $ unDegradeBy x pat)
 
+{- | As 'sometimesBy', but applies the given transformation to the pattern in its entirety
+before filtering its actual appearances. Less efficient than 'sometimesBy' but may
+be useful when the passed pattern transformation depends on properties of the
+pattern before probabilities are taken into account.
+
+@
+'sometimes'' = sometimesBy' 0.5
+'often'' = sometimesBy' 0.75
+'rarely'' = sometimesBy' 0.25
+'almostNever'' = sometimesBy' 0.1
+'almostAlways'' = sometimesBy' 0.9
+@
+-}
 sometimesBy' :: Pattern Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 sometimesBy' x f pat = overlay (degradeBy x pat) (unDegradeBy x $ f pat)
 
--- | @sometimes@ is an alias for sometimesBy 0.5.
+-- | @sometimes@ is an alias for @sometimesBy 0.5@.
 sometimes :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 sometimes = sometimesBy 0.5
 
 sometimes' :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 sometimes' = sometimesBy' 0.5
 
--- | @often@ is an alias for sometimesBy 0.75.
+-- | @often@ is an alias for @sometimesBy 0.75@.
 often :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 often = sometimesBy 0.75
 
 often' :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 often' = sometimesBy' 0.75
 
--- | @rarely@ is an alias for sometimesBy 0.25.
+-- | @rarely@ is an alias for @sometimesBy 0.25@.
 rarely :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 rarely = sometimesBy 0.25
 
 rarely' :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 rarely' = sometimesBy' 0.25
 
--- | @almostNever@ is an alias for sometimesBy 0.1
+-- | @almostNever@ is an alias for @sometimesBy 0.1@.
 almostNever :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 almostNever = sometimesBy 0.1
 
 almostNever' :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 almostNever' = sometimesBy 0.1
 
--- | @almostAlways@ is an alias for sometimesBy 0.9
+-- | @almostAlways@ is an alias for @sometimesBy 0.9@.
 almostAlways :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 almostAlways = sometimesBy 0.9
 
 almostAlways' :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 almostAlways' = sometimesBy' 0.9
 
+{-|
+Never apply a transformation, returning the pattern unmodified.
+
+@never = flip const@
+-}
+
 never :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 never = flip const
 
+{-|
+Apply the transformation to the pattern unconditionally.
+
+@always = id@
+-}
 always :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 always = id
 
-{- | @someCyclesBy@ is a cycle-by-cycle version of @sometimesBy@. It has a
-`someCycles = someCyclesBy 0.5` alias -}
+{- | @someCyclesBy@ is a cycle-by-cycle version of @'sometimesBy'@.
+
+@someCycles = someCyclesBy 0.5@
+-}
 someCyclesBy :: Pattern Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 someCyclesBy pd f pat = innerJoin $ (\d -> _someCyclesBy d f pat) <$> pd
 
@@ -329,40 +435,21 @@
 somecyclesBy :: Pattern Double -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 somecyclesBy = someCyclesBy
 
+-- | @someCycles = someCyclesBy 0.5@
 someCycles :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 someCycles = someCyclesBy 0.5
 
 somecycles :: (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 somecycles = someCycles
 
-{- | `degrade` randomly removes events from a pattern 50% of the time:
+-- ** Pattern transformations
 
-@
-d1 $ slow 2 $ degrade $ sound "[[[feel:5*8,feel*3] feel:3*8], feel*4]"
-   # accelerate "-6"
-   # speed "2"
-@
+{- |
+Pattern transformations are functions generally of type @'Pattern' a -> 'Pattern' a@.
+This means they take a pattern of any type and return a pattern of that type.
 
-The shorthand syntax for `degrade` is a question mark: `?`. Using `?`
-will allow you to randomly remove events from a portion of a pattern:
 
-@
-d1 $ slow 2 $ sound "bd ~ sn bd ~ bd? [sn bd?] ~"
-@
-
-You can also use `?` to randomly remove events from entire sub-patterns:
-
-@
-d1 $ slow 2 $ sound "[[[feel:5*8,feel*3] feel:3*8]?, feel*4]"
-@
--}
-degrade :: Pattern a -> Pattern a
-degrade = _degradeBy 0.5
-
-{- | (The above means that `brak` is a function from patterns of any type,
-to a pattern of the same type.)
-
-Make a pattern sound a bit like a breakbeat
+This transformation makes a pattern sound a bit like a breakbeat.
 
 Example:
 
@@ -414,25 +501,6 @@
 palindrome :: Pattern a -> Pattern a
 palindrome p = slowAppend p (rev p)
 
--- | Composing patterns
-
-{- | The function @seqP@ allows you to define when
-a sound within a list starts and ends. The code below contains three
-separate patterns in a `stack`, but each has different start times
-(zero cycles, eight cycles, and sixteen cycles, respectively). All
-patterns stop after 128 cycles:
-
-@
-d1 $ seqP [
-  (0, 128, sound "bd bd*2"),
-  (8, 128, sound "hh*2 [sn cp] cp future*4"),
-  (16, 128, sound (samples "arpy*8" (run 16)))
-]
-@
--}
-seqP :: [(Time, Time, Pattern a)] -> Pattern a
-seqP ps = stack $ map (\(s, e, p) -> playFor s e (sam s `rotR` p)) ps
-
 -- | Degrades a pattern over the given time.
 fadeOut :: Time -> Pattern a -> Pattern a
 fadeOut dur p = innerJoin $ (`_degradeBy` p) <$> _slow dur envL
@@ -441,7 +509,7 @@
 fadeOutFrom :: Time -> Time -> Pattern a -> Pattern a
 fadeOutFrom from dur p = innerJoin $ (`_degradeBy` p) <$> (from `rotR` _slow dur envL)
 
--- | 'Undegrades' a pattern over the given time.
+-- | ’Undegrades’ a pattern over the given time.
 fadeIn :: Time -> Pattern a -> Pattern a
 fadeIn dur p = innerJoin $ (`_degradeBy` p) <$> _slow dur envLR
 
@@ -467,7 +535,7 @@
 d1 $ slow 2 $ sound "ho ho:2 ho:3 hc"
 @
 
-Or by four thirds (i.e. speeding it up by a third; `4%3` means four over
+Or by four thirds (i.e. speeding it up by a third; @4%3@ means four over
 three):
 
 @
@@ -481,8 +549,9 @@
 d1 $ spread slow [2,4%3] $ sound "ho ho:2 ho:3 hc"
 @
 
-Note that if you pass ($) as the function to spread values over, you
-can put functions as the list of values. For example:
+Note that if you pass @($)@ as the function to spread values over, you
+can put functions as the list of values. ('spreadf' is an alias for @spread ($)@.)
+For example:
 
 @
 d1 $ spread ($) [density 2, rev, slow 2, striate 3, (# speed "0.8")]
@@ -491,38 +560,41 @@
 
 Above, the pattern will have these transforms applied to it, one at a time, per cycle:
 
-* cycle 1: `density 2` - pattern will increase in speed
-* cycle 2: `rev` - pattern will be reversed
-* cycle 3: `slow 2` - pattern will decrease in speed
-* cycle 4: `striate 3` - pattern will be granualized
-* cycle 5: `(# speed "0.8")` - pattern samples will be played back more slowly
+* cycle 1: @density 2@ - pattern will increase in speed
+* cycle 2: @rev@ - pattern will be reversed
+* cycle 3: @slow 2@ - pattern will decrease in speed
+* cycle 4: @striate 3@ - pattern will be granualized
+* cycle 5: @(# speed "0.8")@ - pattern samples will be played back more slowly
 
-After `(# speed "0.8")`, the transforms will repeat and start at `density 2` again.
+After @(# speed "0.8")@, the transforms will repeat and start at @density 2@ again.
 -}
 spread :: (a -> t -> Pattern b) -> [a] -> t -> Pattern b
 spread f xs p = slowcat $ map (`f` p) xs
 
+-- | An alias for 'spread' consistent with 'fastspread'.
 slowspread :: (a -> t -> Pattern b) -> [a] -> t -> Pattern b
 slowspread = spread
 
-{- | @fastspread@ works the same as @spread@, but the result is squashed into a single cycle. If you gave four values to @spread@, then the result would seem to speed up by a factor of four. Compare these two:
+{- | @fastspread@ works the same as `spread`, but the result is squashed into a single cycle. If you gave four values to @spread@, then the result would seem to speed up by a factor of four. Compare these two:
 
-d1 $ spread chop [4,64,32,16] $ sound "ho ho:2 ho:3 hc"
+@
+  d1 $ spread chop [4,64,32,16] $ sound "ho ho:2 ho:3 hc"
 
-d1 $ fastspread chop [4,64,32,16] $ sound "ho ho:2 ho:3 hc"
+  d1 $ fastspread chop [4,64,32,16] $ sound "ho ho:2 ho:3 hc"
+@
 
-There is also @slowspread@, which is an alias of @spread@.
+There is also `slowspread`, which is an alias of @spread@.
 -}
 fastspread :: (a -> t -> Pattern b) -> [a] -> t -> Pattern b
 fastspread f xs p = fastcat $ map (`f` p) xs
 
-{- | There's a version of this function, `spread'` (pronounced "spread prime"), which takes a *pattern* of parameters, instead of a list:
+{- | There's a version of this function, `spread'` (pronounced "spread prime"), which takes a /pattern/ of parameters, instead of a list:
 
 @
 d1 $ spread' slow "2 4%3" $ sound "ho ho:2 ho:3 hc"
 @
 
-This is quite a messy area of Tidal - due to a slight difference of
+This is quite a messy area of Tidal—due to a slight difference of
 implementation this sounds completely different! One advantage of
 using `spread'` though is that you can provide polyphonic parameters, e.g.:
 
@@ -533,14 +605,14 @@
 spread' :: Monad m => (a -> b -> m c) -> m a -> b -> m c
 spread' f vpat pat = vpat >>= \v -> f v pat
 
-{- | `spreadChoose f xs p` is similar to `slowspread` but picks values from
-`xs` at random, rather than cycling through them in order. It has a
-shorter alias `spreadr`.
+{- | @spreadChoose f xs p@ is similar to `slowspread` but picks values from
+`xs` at random, rather than cycling through them in order.
 -}
 spreadChoose :: (t -> t1 -> Pattern b) -> [t] -> t1 -> Pattern b
 spreadChoose f vs p = do v <- _segment 1 (choose vs)
                          f v p
 
+-- | A shorter alias for 'spreadChoose'.
 spreadr :: (t -> t1 -> Pattern b) -> [t] -> t1 -> Pattern b
 spreadr = spreadChoose
 
@@ -553,7 +625,7 @@
   sound "hh hc"
 @
 
-This will apply `striate 4` for every _even_ cycle and aply `# coarse "24 48"` for every _odd_.
+This will apply @'striate' 4@ for every _even_ cycle and apply @# coarse "24 48"@ for every _odd_.
 
 Detail: As you can see the test function is arbitrary and does not rely on anything tidal specific. In fact it uses only plain haskell functionality, that is: it calculates the modulo of 2 of the current cycle which is either 0 (for even cycles) or 1. It then compares this value against 0 and returns the result, which is either `True` or `False`. This is what the `ifp` signature's first part signifies `(Int -> Bool)`, a function that takes a whole number and returns either `True` or `False`.
 -}
@@ -704,19 +776,24 @@
         , filterWhen (\t -> not $ cyclePos t >= s && cyclePos t < e) p
         ]
 
+{-|
+Reverse the part of the pattern sliced out by the @(start, end)@ pair.
+
+@revArc a = within a rev@
+-}
 revArc :: (Time, Time) -> Pattern a -> Pattern a
 revArc a = within a rev
 
-{- | You can use the @e@ function to apply a Euclidean algorithm over a
+{- | You can use the @euclid@ function to apply a Euclidean algorithm over a
 complex pattern, although the structure of that pattern will be lost:
 
 @
-d1 $ e 3 8 $ sound "bd*2 [sn cp]"
+d1 $ euclid 3 8 $ sound "bd*2 [sn cp]"
 @
 
 In the above, three sounds are picked from the pattern on the right according
-to the structure given by the `e 3 8`. It ends up picking two `bd` sounds, a
-`cp` and missing the `sn` entirely.
+to the structure given by the @euclid 3 8@. It ends up picking two @bd@ sounds, a
+@cp@ and missing the @sn@ entirely.
 
 A negative first argument provides the inverse of the euclidean pattern.
 
@@ -727,7 +804,7 @@
 more about this in the paper
 [The Euclidean Algorithm Generates Traditional Musical Rhythms](http://cgm.cs.mcgill.ca/~godfried/publications/banff.pdf)
 by Toussaint. Some examples from this paper are included below,
-including rotation in some cases.
+including rotation as a third parameter in some cases (see 'euclidOff').
 
 @
 - (2,5) : A thirteenth century Persian rhythm called Khafif-e-ramal.
@@ -752,6 +829,9 @@
 - (11,24,14) : A rhythm necklace of the Aka Pygmies of Central Africa.
 - (13,24,5) : Another rhythm necklace of the Aka Pygmies of the upper Sangha.
 @
+
+There was once a shorter alias @e@ for this function. It has been removed, but you
+may see references to it in older Tidal code.
 -}
 euclid :: Pattern Int -> Pattern Int -> Pattern a -> Pattern a
 euclid = tParam2 _euclid
@@ -760,19 +840,35 @@
 _euclid n k a | n >= 0 = fastcat $ fmap (bool silence a) $ bjorklund (n,k)
               | otherwise = fastcat $ fmap (bool a silence) $ bjorklund (-n,k)
 
-{- | `euclidfull n k pa pb` stacks @e n k pa@ with @einv n k pb@ -}
+{- |
+
+@euclidFull n k pa pb@ stacks @'euclid' n k pa@ with @'euclidInv' n k pb@. For example,
+to implement the traditional flamenco rhythm, you could use hard claps for the former
+and soft claps for the latter:
+
+@d1 $ euclidFull 3 7 "realclaps" ("realclaps" # gain 0.8)@
+
+-}
 euclidFull :: Pattern Int -> Pattern Int -> Pattern a -> Pattern a -> Pattern a
 euclidFull n k pa pb = stack [ euclid n k pa, euclidInv n k pb ]
 
-_euclidBool :: Int -> Int -> Pattern Bool
+-- | Less expressive than 'euclid' due to its constrained types, but may be more efficient.
+_euclidBool :: Int -> Int -> Pattern Bool -- TODO: add 'euclidBool'?
 _euclidBool n k = fastFromList $ bjorklund (n,k)
 
 _euclid' :: Int -> Int -> Pattern a -> Pattern a
 _euclid' n k p = fastcat $ map (\x -> if x then p else silence) (bjorklund (n,k))
 
+{- |
+
+As 'euclid', but taking a third rotational parameter corresponding to the onset
+at which to start the rhythm.
+
+-}
 euclidOff :: Pattern Int -> Pattern Int -> Pattern Int -> Pattern a -> Pattern a
 euclidOff = tParam3 _euclidOff
 
+-- | A shorter alias for 'euclidOff'.
 eoff :: Pattern Int -> Pattern Int -> Pattern Int -> Pattern a -> Pattern a
 eoff = euclidOff
 
@@ -780,6 +876,7 @@
 _euclidOff _ 0 _ _ = silence
 _euclidOff n k s p = (rotL $ fromIntegral s%fromIntegral k) (_euclid n k p)
 
+-- | As 'euclidOff', but specialized to 'Bool'. May be more efficient than 'euclidOff'.
 euclidOffBool :: Pattern Int -> Pattern Int -> Pattern Int -> Pattern Bool -> Pattern Bool
 euclidOffBool = tParam3 _euclidOffBool
 
@@ -802,11 +899,9 @@
     layers = map bjorklund . (zip<*>tail)
     boolsToPat a b' = flip const <$> filterValues (== True) (fastFromList a) <* b'
 
-{- | `euclidInv` fills in the blanks left by `e`
- -
- @e 3 8 "x"@ -> @"x ~ ~ x ~ ~ x ~"@
+{- | `euclidInv` fills in the blanks left by `euclid`.
 
- @euclidInv 3 8 "x"@ -> @"~ x x ~ x x ~ x"@
+Whereas @euclid 3 8 "x"@ produces @"x ~ ~ x ~ ~ x ~"@, @euclidInv 3 8 "x"@ produces @"~ x x ~ x x ~ x"@.
 -}
 euclidInv :: Pattern Int -> Pattern Int -> Pattern a -> Pattern a
 euclidInv = tParam2 _euclidInv
@@ -926,7 +1021,7 @@
 rot :: Ord a => Pattern Int -> Pattern a -> Pattern a
 rot = tParam _rot
 
--- Calculates a whole cycle, rotates it, then constrains events to the original query arc
+-- | Calculates a whole cycle, rotates it, then constrains events to the original query arc.
 _rot :: Ord a => Int -> Pattern a -> Pattern a
 _rot i pat = splitQueries $ pat {query = \st -> f st (query pat (st {arc = wholeCycle (arc st)}))}
   where -- TODO maybe events with the same arc (part+whole) should be
@@ -947,7 +1042,7 @@
             p' <- subArc (part e) a
             return e {part = p'}
 
--- | @segment n p@: 'samples' the pattern @p@ at a rate of @n@
+-- | @segment n p@: ’samples’ the pattern @p@ at a rate of @n@
 -- events per cycle. Useful for turning a continuous pattern into a
 -- discrete one.
 segment :: Pattern Time -> Pattern a -> Pattern a
@@ -960,14 +1055,6 @@
 discretise :: Pattern Time -> Pattern a -> Pattern a
 discretise = segment
 
--- | @randcat ps@: does a @slowcat@ on the list of patterns @ps@ but
--- randomises the order in which they are played.
-randcat :: [Pattern a] -> Pattern a
-randcat ps = spread' rotL (_segment 1 $ (% 1) . fromIntegral <$> (_irand (length ps) :: Pattern Int)) (slowcat ps)
-
-wrandcat :: [(Pattern a, Double)] -> Pattern a
-wrandcat ps = unwrap $ wchooseBy (segment 1 rand) ps
-
 -- @fromNote p@: converts a pattern of human-readable pitch names
 -- into pitch numbers. For example, @"cs2"@ will be parsed as C Sharp
 -- in the 2nd octave with the result of @11@, and @"b-3"@ as
@@ -1013,13 +1100,14 @@
 The above fits three samples into the pattern, i.e. for the first cycle this will be `"bd"`, `"sn"` and `"arpy"`, giving the result `"bd [~ sn] arpy sn"` (note that we start counting at zero, so that `0` picks the first value). The following cycle the *next* three values in the list will be picked, i.e. `"arpy:1"`, `"casio"` and `"bd"`, giving the pattern `"arpy:1 [~ casio] bd casio"` (note that the list wraps round here).
 
 -}
+fit :: Pattern Int -> [a] -> Pattern Int -> Pattern a
+fit pint xs p = (tParam func) pint (xs,p)
+  where func i (xs',p') = _fit i xs' p'
+
 _fit :: Int -> [a] -> Pattern Int -> Pattern a
 _fit perCycle xs p = (xs !!!) <$> (p {query = map (\e -> fmap (+ pos e) e) . query p})
   where pos e = perCycle * floor (start $ part e)
 
-fit :: Pattern Int -> [a] -> Pattern Int -> Pattern a
-fit pint xs p = (tParam func) pint (xs,p)
-  where func i (xs',p') = _fit i xs' p'
 
 permstep :: RealFrac b => Int -> [a] -> Pattern b -> Pattern a
 permstep nSteps things p = unwrap $ (\n -> fastFromList $ concatMap (\x -> replicate (fst x) (snd x)) $ zip (ps !! floor (n * fromIntegral (length ps - 1))) things) <$> _segment 1 p
@@ -1086,10 +1174,10 @@
 _stripe :: Int -> Pattern a -> Pattern a
 _stripe = substruct' . randStruct
 
--- | @slowstripe n p@: The same as @stripe@, but the result is also
+-- | @slowstripe n p@ is the same as @stripe@, but the result is also
 -- @n@ times slower, so that the mean average duration of the stripes
 -- is exactly one cycle, and every @n@th stripe starts on a cycle
--- boundary (in indian classical terms, the @sam@).
+-- boundary (in Indian classical terms, the /sam/).
 slowstripe :: Pattern Int -> Pattern a -> Pattern a
 slowstripe n = slow (toRational <$> n) . stripe n
 
@@ -1107,9 +1195,9 @@
 parseLMRule' str = map fixer $ parseLMRule str
   where fixer (c,r) = (head c, r)
 
-{- | returns the `n`th iteration of a [Lindenmayer System](https://en.wikipedia.org/wiki/L-system) with given start sequence.
+{- | Returns the `n`th iteration of a [Lindenmayer System](https://en.wikipedia.org/wiki/L-system) with given start sequence.
 
-for example:
+An example
 
 @
 lindenmayer 1 "a:b,b:ab" "ab" -> "bab"
@@ -1257,18 +1345,50 @@
 _chunk' :: Integral a => a -> (Pattern b -> Pattern b) -> Pattern b -> Pattern b
 _chunk' n f p = _chunk (-n) f p
 
+{-|
+@inside@ carries out an operation /inside/ a cycle.
+For example, while @rev "0 1 2 3 4 5 6 7"@ is the same as @"7 6 5 4 3 2 1 0"@,
+@inside 2 rev "0 1 2 3 4 5 6 7"@ gives @"3 2 1 0 7 6 5 4"@.
+-}
+inside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
+inside np f p = innerJoin $ (\n -> _inside n f p) <$> np
+
 _inside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
 _inside n f p = _fast n $ f (_slow n p)
 
-inside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
-inside np f p = innerJoin $ (\n -> _inside n f p) <$> np
+{-|
+@outside@ is the inverse of the 'inside' function. @outside@ applies its function /outside/ the cycle.
+Say you have a pattern that takes 4 cycles to repeat and apply the rev function:
 
-_outside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
-_outside n = _inside (1/n)
+@
+d1 $ rev $ cat [s "bd bd sn",s "sn sn bd", s"lt lt sd", s "sd sd bd"]
+@
 
+The above generates:
+
+@
+d1 $ rev $ cat [s "sn bd bd",s "bd sn sn", s "sd lt lt", s "bd sd sd"]
+@
+
+However if you apply @outside@:
+
+@
+d1 $ outside 4 (rev) $ cat [s "bd bd sn",s "sn sn bd", s"lt lt sd", s "sd sd bd"]
+@
+
+The result is:
+
+@
+d1 $ rev $ cat [s "bd sd sd", s "sd lt lt", s "sn sn bd", s "bd bd sn"]
+@
+
+-}
 outside :: Pattern Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
 outside np f p = innerJoin $ (\n -> _outside n f p) <$> np
 
+_outside :: Time -> (Pattern a1 -> Pattern a) -> Pattern a1 -> Pattern a
+_outside n = _inside (1/n)
+
 loopFirst :: Pattern a -> Pattern a
 loopFirst p = splitQueries $ p {query = f}
   where f st = map
@@ -1287,28 +1407,41 @@
   where minT = minimum $ map (\(x,_,_) -> x) ps
         maxT = maximum $ map (\(_,x,_) -> x) ps
 
-{- | @toScale@ lets you turn a pattern of notes within a scale (expressed as a
-list) to note numbers.  For example `toScale [0, 4, 7] "0 1 2 3"` will turn
-into the pattern `"0 4 7 12"`.  It assumes your scale fits within an octave;
-to change this use `toScale' size`.  Example:
-`toScale' 24 [0,4,7,10,14,17] (run 8)` turns into `"0 4 7 10 14 17 24 28"`
+{-|
+@toScale@ lets you turn a pattern of notes within a scale (expressed as a
+list) to note numbers.
+
+For example: @toScale [0, 4, 7] "0 1 2 3"@ will turn
+into the pattern @"0 4 7 12"@.
+
+This function assumes your scale fits within an
+octave; if that's not true, use 'toScale''.
+
+@toScale = toScale' 12@
 -}
+toScale :: Num a => [a] -> Pattern Int -> Pattern a
+toScale = toScale' 12
+
+{- | As 'toScale', though allowing scales of arbitrary size.
+
+An example: @toScale' 24 [0,4,7,10,14,17] (run 8)@ turns into @"0 4 7 10 14 17 24 28"@.
+-}
 toScale' :: Num a => Int -> [a] -> Pattern Int -> Pattern a
 toScale' _ [] = const silence
 toScale' o s = fmap noteInScale
   where octave x = x `div` length s
         noteInScale x = (s !!! x) + fromIntegral (o * octave x)
 
-toScale :: Num a => [a] -> Pattern Int -> Pattern a
-toScale = toScale' 12
 
 {- | `swingBy x n` divides a cycle into `n` slices and delays the notes in
-the second half of each slice by `x` fraction of a slice . @swing@ is an alias
-for `swingBy (1%3)`
+the second half of each slice by `x` fraction of a slice.
 -}
 swingBy :: Pattern Time -> Pattern Time -> Pattern a -> Pattern a
 swingBy x n = inside n (withinArc (Arc 0.5 1) (x ~>))
 
+{-|
+As 'swingBy', with the cycle division set to ⅓.
+-}
 swing :: Pattern Time -> Pattern a -> Pattern a
 swing = swingBy (pure $ 1%3)
 
@@ -1325,10 +1458,10 @@
     nT :: Time
     nT = fromIntegral n
 
-{- | `shuffle n p` evenly divides one cycle of the pattern `p` into `n` parts,
+{- | @shuffle n p@ evenly divides one cycle of the pattern @p@ into @n@ parts,
 and returns a random permutation of the parts each cycle.  For example,
-`shuffle 3 "a b c"` could return `"a b c"`, `"a c b"`, `"b a c"`, `"b c a"`,
-`"c a b"`, or `"c b a"`.  But it will **never** return `"a a a"`, because that
+@shuffle 3 "a b c"@ could return @"a b c"@, @"a c b"@, @"b a c"@, @"b c a"@,
+@"c a b"@, or @"c b a"@.  But it will **never** return @"a a a"@, because that
 is not a permutation of the parts.
 -}
 shuffle :: Pattern Int -> Pattern a -> Pattern a
@@ -1337,10 +1470,10 @@
 _shuffle :: Int -> Pattern a -> Pattern a
 _shuffle n = _rearrangeWith (randrun n) n
 
-{- | `scramble n p` is like `shuffle` but randomly selects from the parts
-of `p` instead of making permutations.
-For example, `scramble 3 "a b c"` will randomly select 3 parts from
-`"a"` `"b"` and `"c"`, possibly repeating a single part.
+{- | @scramble n p@ is like 'shuffle' but randomly selects from the parts
+of @p@ instead of making permutations.
+For example, @scramble 3 "a b c"@ will randomly select 3 parts from
+@"a"@ @"b"@ and @"c"@, possibly repeating a single part.
 -}
 scramble :: Pattern Int -> Pattern a -> Pattern a
 scramble = tParam _scramble
@@ -1348,6 +1481,16 @@
 _scramble :: Int -> Pattern a -> Pattern a
 _scramble n = _rearrangeWith (_segment (fromIntegral n) $ _irand n) n
 
+{-|
+@randrun n@ generates a pattern of random integers less than @n@.
+
+The following plays random notes in an octave:
+
+@
+d1 $ s "superhammond!12" # n (fromIntegral <$> randrun 13)
+@
+
+-}
 randrun :: Int -> Pattern Int
 randrun 0 = silence
 randrun n' =
@@ -1360,7 +1503,61 @@
                 toEv (a',v) = do a'' <- subArc a a'
                                  return $ Event (Context []) (Just a') a'' v
 
+-- ** Composing patterns
 
+{- | The function @seqP@ allows you to define when
+a sound within a list starts and ends. The code below contains three
+separate patterns in a `stack`, but each has different start times
+(zero cycles, eight cycles, and sixteen cycles, respectively). All
+patterns stop after 128 cycles:
+
+@
+d1 $ seqP [
+  (0, 128, sound "bd bd*2"),
+  (8, 128, sound "hh*2 [sn cp] cp future*4"),
+  (16, 128, sound (samples "arpy*8" (run 16)))
+]
+@
+-}
+seqP :: [(Time, Time, Pattern a)] -> Pattern a
+seqP ps = stack $ map (\(s, e, p) -> playFor s e (sam s `rotR` p)) ps
+
+{-|
+The @ur@ function is designed for longer form composition, by allowing you to
+create ’patterns of patterns’ in a repeating loop. It takes four parameters:
+how long the loop will take, a pattern giving the structure of the composition,
+a lookup table for named patterns to feed into that structure, and a second
+lookup table for named transformations\/effects.
+
+The /ur-/ prefix [comes from German](https://en.wiktionary.org/wiki/ur-#German) and
+means /proto-/ or /original/. For a mnemonic device, think of this function as
+assembling a set of original patterns (ur-patterns) into a larger, newer whole.
+
+Lets say you had three patterns (called @a@, @b@ and @c@), and that you wanted
+to play them four cycles each, over twelve cycles in total. Here is one way to
+do it:
+
+@
+let pats =
+      [
+        ("a", stack [n "c4 c5 g4 f4 f5 g4 e5 g4" # s "superpiano" # gain "0.7",
+                     n "[c3,g4,c4]" # s "superpiano"# gain "0.7"
+                    ]
+        ),
+        ("b", stack [n "d4 c5 g4 f4 f5 g4 e5 g4" # s "superpiano" # gain "0.7",
+                     n "[d3,a4,d4]" # s "superpiano"# gain "0.7"
+                    ]
+        ),
+        ("c", stack [n "f4 c5 g4 f4 f5 g4 e5 g4" # s "superpiano" # gain "0.7",
+                     n "[f4,c5,f4]" # s "superpiano"# gain "0.7"
+                    ]
+        )
+      ]
+in
+d1 $ ur 12 "a b c" pats []
+@
+
+-}
 ur :: Time -> Pattern String -> [(String, Pattern a)] -> [(String, Pattern a -> Pattern a)] -> Pattern a
 ur t outer_p ps fs = _slow t $ unwrap $ adjust <$> timedValues (getPat . split <$> outer_p)
   where split = wordsBy (==':')
@@ -1376,10 +1573,11 @@
         matchF str = fromMaybe id $ lookup str fs
         timedValues = withEvent (\(Event c (Just a) a' v) -> Event c (Just a) a' (a,v)) . filterDigital
 
+-- | A simpler version of 'ur' that just provides name-value bindings that are reflected in the provided pattern.
 inhabit :: [(String, Pattern a)] -> Pattern String -> Pattern a
 inhabit ps p = squeezeJoin $ (\s -> fromMaybe silence $ lookup s ps) <$> p
 
-{- | @spaceOut xs p@ repeats a pattern @p@ at different durations given by the list of time values in @xs@ -}
+{- | @spaceOut xs p@ repeats a 'Pattern' @p@ at different durations given by the list of time values in @xs@. -}
 spaceOut :: [Time] -> Pattern a -> Pattern a
 spaceOut xs p = _slow (toRational $ sum xs) $ stack $ map (`compressArc` p) spaceArcs
   where markOut :: Time -> [Time] -> [Arc]
@@ -1388,13 +1586,13 @@
         spaceArcs = map (\(Arc a b) -> Arc (a/s) (b/s)) $ markOut 0 xs
         s = sum xs
 
--- | @flatpat@ takes a Pattern of lists and pulls the list elements as
--- separate Events
+-- | @flatpat@ takes a 'Pattern' of lists and pulls the list elements as
+-- separate 'Event's.
 flatpat :: Pattern [a] -> Pattern a
 flatpat p = p {query = concatMap (\(Event c b b' xs) -> map (Event c b b') xs) . query p}
 
--- | @layer@ takes a Pattern of lists and pulls the list elements as
--- separate Events
+-- | @layer@ takes a list of 'Pattern'-returning functions and a seed element,
+-- stacking the result of applying the seed element to each function in the list.
 layer :: [a -> Pattern b] -> a -> Pattern b
 layer fs p = stack $ map ($ p) fs
 
@@ -1422,6 +1620,20 @@
         -- TODO ignoring analog events.. Should we just leave them as-is?
         shiftIt _ _ _ = Nothing
 
+
+{-| The @arp@ function takes an additional pattern of arpeggiate modes. For example:
+
+@
+d1 $ sound "superpiano" # n (arp "<up down diverge>" "<a'm9'8 e'7sus4'8>")
+@
+
+The different arpeggiate modes are:
+@
+up down updown downup up&down down&up converge
+diverge disconverge pinkyup pinkyupdown
+thumbup thumbupdown-
+@
+-}
 arp :: Pattern String -> Pattern a -> Pattern a
 arp = tParam _arp
 
@@ -1453,19 +1665,26 @@
           where thumb = head xs
 
 {- | `rolled` plays each note of a chord quickly in order, as opposed to simultaneously; to give a chord a harp-like effect.
-This will played from the lowest note to the highest note of the chord
+This will played from the lowest note to the highest note of the chord:
 @
 rolled $ n "c'maj'4" # s "superpiano"
 @
 
+@rolled = rolledBy (1/4)@
+-}
+rolled :: Pattern a -> Pattern a
+rolled = rolledBy (1/4)
 
-And you can use `rolledBy` or `rolledBy'` to specify the length of the roll. The value in the passed pattern
+{-
+As 'rolled', but allowing you to specify the length of the roll. The value in the passed pattern
 is the divisor of the cycle length. A negative value will play the arpeggio in reverse order.
 
 @
 rolledBy "<1 -0.5 0.25 -0.125>" $ note "c'maj9" # s "superpiano"
 @
 -}
+rolledBy :: Pattern (Ratio Integer) -> Pattern a -> Pattern a
+rolledBy pt = tParam rolledWith (segment 1 $ pt)
 
 rolledWith :: Ratio Integer -> Pattern a -> Pattern a
 rolledWith t = withEvents aux
@@ -1481,12 +1700,6 @@
                             dur = ((e - s)) / ((1/ (abs t))*fromIntegral d)
                shiftIt _ _ ev =  return ev
 
-rolledBy :: Pattern (Ratio Integer) -> Pattern a -> Pattern a
-rolledBy pt = tParam rolledWith (segment 1 $ pt)
-
-rolled :: Pattern a -> Pattern a
-rolled = rolledBy (1/4)
-
 {- TODO !
 
 -- | @fill@ 'fills in' gaps in one pattern with events from another. For example @fill "bd" "cp ~ cp"@ would result in the equivalent of `"~ bd ~"`. This only finds gaps in a resulting pattern, in other words @"[bd ~, sn]"@ doesn't contain any gaps (because @sn@ covers it all), and @"bd ~ ~ sn"@ only contains a single gap that bridges two steps.
@@ -1509,14 +1722,14 @@
     tolerance = 0.01
 -}
 
--- Repeats each event @n@ times within its arc
+-- | @ply n@ repeats each event @n@ times within its arc.
 ply :: Pattern Rational -> Pattern a -> Pattern a
 ply = tParam _ply
 
 _ply :: Rational -> Pattern a -> Pattern a
 _ply n pat = squeezeJoin $ (_fast n . pure) <$> pat
 
--- Like ply, but applies a function each time. The applications are compounded.
+-- | As 'ply', but applies a function each time. The applications are compounded.
 plyWith :: (Ord t, Num t) => Pattern t -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 plyWith np f p = innerJoin $ (\n -> _plyWith n f p) <$> np
 
@@ -1556,6 +1769,19 @@
 while :: Pattern Bool -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 while b f pat = sew b (f pat) pat
 
+{-|
+@stutter n t pat@ repeats each event in @pat@ @n@ times, separated by @t@ time (in fractions of a cycle).
+It is like 'Sound.Tidal.Control.echo' that doesn't reduce the volume, or 'ply' if you controlled the timing.
+
+@
+d1 $ stutter 4 (1/16) $ s "bd cp"
+@
+
+is functionally equivalent to
+@
+d1 $ stut 4 1 (1/16) $ s "bd cp"
+@
+-}
 stutter :: Integral i => i -> Time -> Pattern a -> Pattern a
 stutter n t p = stack $ map (\i -> (t * fromIntegral i) `rotR` p) [0 .. (n-1)]
 
@@ -1645,14 +1871,37 @@
      -> Pattern ValueMap
 juxBy n f p = stack [p |+ P.pan 0.5 |- P.pan (n/2), f $ p |+ P.pan 0.5 |+ P.pan (n/2)]
 
+{- |
+Given a sample's directory name and number, this generates a string
+suitable to pass to 'Data.String.fromString' to create a 'Pattern String'.
+'samples' is a 'Pattern'-compatible interface to this function.
+
+@pick name n = name ++ ":" ++ show n@
+-}
 pick :: String -> Int -> String
 pick name n = name ++ ":" ++ show n
 
--- samples "jvbass [~ latibro] [jvbass [latibro jvbass]]" ((1%2) `rotL` slow 6 "[1 6 8 7 3]")
+{- |
+Given a pattern of sample directory names and a of pattern indices
+create a pattern of strings corresponding to the sample at each
+name-index pair.
 
+An example:
+@samples "jvbass [~ latibro] [jvbass [latibro jvbass]]" ((1%2) `rotL` slow 6 "[1 6 8 7 3]")@
+
+The type signature is more general here, but you can consider this
+to be a function of type @Pattern String -> Pattern Int -> Pattern String.
+
+@samples = liftA2 pick@
+-}
 samples :: Applicative f => f String -> f Int -> f String
 samples p p' = pick <$> p <*> p'
 
+{- |
+Equivalent to 'samples', though the sample specifier pattern
+(the @f Int@) will be evaluated first. Not a large difference
+in the majority of cases.
+-}
 samples' :: Applicative f => f String -> f Int -> f String
 samples' p p' = flip pick <$> p' <*> p
 
@@ -1668,6 +1917,11 @@
                               ) (arc p a)
 -}
 
+{-
+ As 'spread', but specialized so that the list contains functions returning patterns.
+
+@spreadf = 'spread' ($)@
+-}
 spreadf :: [a -> Pattern b] -> a -> Pattern b
 spreadf = spread ($)
 
@@ -1728,23 +1982,31 @@
               | otherwise = silence
 
 
--- ghost'' is kept for backwards compatibility
+-- | Deprecated backwards-compatible alias for 'ghostWith'.
 ghost'' :: Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 ghost'' = ghostWith
 
+-- | Like 'ghost'', but a user-supplied function describes how to alter the pattern.
 ghostWith :: Time -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 ghostWith a f p = superimpose (((a*2.5) `rotR`) . f) $ superimpose (((a*1.5) `rotR`) . f) p
 
+{-
+@ghost' t pat@ Adds quieter, pitch-shifted, copies of an event @t@ cycles after events in @pat@, emulating ghost notes that are common in drumming patterns.
+-}
 ghost' :: Time -> Pattern ValueMap -> Pattern ValueMap
 ghost' a p = ghostWith a ((|*| P.gain (pure 0.7)) . (|> P.end (pure 0.2)) . (|*| P.speed (pure 1.25))) p
 
+{-| As 'ghost', but with the copies set to appear one-eighth of a cycle afterwards.
+
+@ghost = ghost' 0.125@
+-}
 ghost :: Pattern ValueMap -> Pattern ValueMap
 ghost = ghost' 0.125
 
-{- |
-   tabby - A more literal weaving than the `weave` function, give number
-   of 'threads' per cycle and two patterns, and this function will weave them
-   together using a plain (aka 'tabby') weave, with a simple over/under structure
+{- | A more literal weaving than the `weave` function. Given @tabby threads p1 p@,
+   parameters representing the threads per cycle and the patterns to weave, and
+   this function will weave them together using a plain (aka ’tabby’) weave,
+   with a simple over/under structure
  -}
 tabby :: Int -> Pattern a -> Pattern a -> Pattern a
 tabby nInt p p' = stack [maskedWarp,
@@ -1781,7 +2043,7 @@
 _pickF :: Int -> [Pattern a -> Pattern a] -> Pattern a -> Pattern a
 _pickF i fs p =  (fs !!! i) p
 
--- | @contrast p f f' p'@ splits controlpattern @p'@ in two, applying
+-- | @contrast p f f' p'@ splits the control pattern @p'@ in two, applying
 -- the function @f@ to one and @f'@ to the other. This depends on
 -- whether events in it contains values matching with those in @p@.
 -- For example in @contrast (# crush 3) (# vowel "a") (n "1") $ n "0 1" # s "bd sn" # speed 3@,
@@ -1838,18 +2100,20 @@
               -> ControlPattern
 unfixRange = contrastRange id
 
--- | limit values in a Pattern (or other Functor) to n equally spaced
+-- | Limits values in a Pattern (or other Functor) to n equally spaced
 -- divisions of 1.
 quantise :: (Functor f, RealFrac b) => b -> f b -> f b
 quantise n = fmap ((/n) . (fromIntegral :: RealFrac b => Int -> b) . round . (*n))
 
--- quantise but with floor
+-- | As 'quantise', but uses 'Prelude.floor' to calculate divisions.
 qfloor :: (Functor f, RealFrac b) => b -> f b -> f b
 qfloor n = fmap ((/n) . (fromIntegral :: RealFrac b => Int -> b) . floor . (*n))
 
+-- | As 'quantise', but uses 'Prelude.ceiling' to calculate divisions.
 qceiling :: (Functor f, RealFrac b) => b -> f b -> f b
 qceiling n = fmap ((/n) . (fromIntegral :: RealFrac b => Int -> b) . ceiling . (*n))
 
+-- | An alias for 'quantise'.
 qround :: (Functor f, RealFrac b) => b -> f b -> f b
 qround = quantise
 
@@ -1858,7 +2122,7 @@
 inv = (not <$>)
 
 -- | Serialises a pattern so there's only one event playing at any one
--- time, making it 'monophonic'. Events which start/end earlier are given priority.
+-- time, making it /monophonic/. Events which start/end earlier are given priority.
 mono :: Pattern a -> Pattern a
 mono p = Pattern $ \(State a cm) -> flatten $ query p (State a cm) where
   flatten :: [Event a] -> [Event a]
@@ -1873,6 +2137,17 @@
   constrainPart e = do a <- subArc (wholeOrPart e) (part e)
                        return $ e {part = a}
 
+{-|
+@smooth@ receives a pattern of numbers and linearly goes from one to the next, passing through all of them. As time is cycle-based, after reaching the last number in the pattern, it will smoothly go to the first one again.
+
+@
+  d1 $ sound "bd*4" # pan (slow 4 $ smooth "0 1 0.5 1")
+@
+
+This sound will pan gradually from left to right, then to the center, then to the right again, and finally comes back to the left.
+
+-}
+
 -- serialize the given pattern
 -- find the middle of the query's arc and use that to query the serialized pattern. We should get either no events or a single event back
 -- if we don't get any events, return nothing
@@ -1908,15 +2183,14 @@
 swap :: Eq a => [(a, b)] -> Pattern a -> Pattern b
 swap things p = filterJust $ (`lookup` things) <$> p
 
-{-
-  snowball |
-  snowball takes a function that can combine patterns (like '+'),
+{-|
+  @snowball@ takes a function that can combine patterns (like '+'),
   a function that transforms a pattern (like 'slow'),
   a depth, and a starting pattern,
-  it will then transform the pattern and combine it with the last transformation until the depth is reached
-  this is like putting an effect (like a filter) in the feedback of a delay line
-  each echo is more effected
-  d1 $ note (scale "hexDorian" $ snowball (+) (slow 2 . rev) 8 "0 ~ . -1 . 5 3 4 . ~ -2") # s "gtr"
+  it will then transform the pattern and combine it with the last transformation until the depth is reached.
+  This is like putting an effect (like a filter) in the feedback of a delay line; each echo is more affected.
+
+  @d1 $ note (scale "hexDorian" $ snowball 8 (+) (slow 2 . rev) "0 ~ . -1 . 5 3 4 . ~ -2") # s "gtr"@
 -}
 snowball :: Int -> (Pattern a -> Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 snowball depth combinationFunction f pattern = cat $ take depth $ scanl combinationFunction pattern $ drop 1 $ iterate f pattern
@@ -1931,6 +2205,8 @@
 soak ::  Int -> (Pattern a -> Pattern a) -> Pattern a -> Pattern a
 soak depth f pattern = cat $ take depth $ iterate f pattern
 
+-- | @construct n p@ breaks @p@ into pieces and then reassembles them
+-- so that it fits into @n@ steps.
 deconstruct :: Int -> Pattern String -> String
 deconstruct n p = intercalate " " $ map showStep $ toList p
   where
@@ -1944,9 +2220,9 @@
             arcs = zip (take n breaks) (drop 1 breaks)
             n' = fromIntegral n
 
-{- @bite@ n ipat pat |
-  slices a pattern `pat` into `n` pieces, then uses the `ipat` pattern of integers to index into those slices.
-  So `bite 4 "0 2*2" (run 8)` is the same as `"[0 1] [4 5]*2"`.
+{- | @bite n ipat pat@ slices a pattern @pat@ into @n@ pieces, then uses the
+  @ipat@ pattern of integers to index into those slices. So @bite 4 "0 2*2" (run
+  8)@ is the same as @"[0 1] [4 5]*2"@.
 -}
 bite :: Pattern Int -> Pattern Int -> Pattern a -> Pattern a
 bite npat ipat pat = innerJoin $ (\n -> _bite n ipat pat) <$> npat
@@ -1956,7 +2232,7 @@
   where zoompat i = zoom (i'/(fromIntegral n), (i'+1)/(fromIntegral n)) pat
            where i' = fromIntegral $ i `mod` n
 
-{- @squeeze@ ipat pats | uses a pattern of integers to index into a list of patterns.
+{- | @squeeze@ uses a pattern of integers to index into a list of patterns.
 -}
 squeeze :: Pattern Int -> [Pattern a] -> Pattern a
 squeeze _ [] = silence
@@ -1980,6 +2256,10 @@
   where zoompat i = zoom (i'/(fromIntegral n), (i'+1)/(fromIntegral n)) (pat)
            where i' = fromIntegral $ i `mod` n
 
+{-|
+@chew@ works the same as 'bite', but speeds up\/slows down playback of sounds as well as squeezing/contracting the slices of the provided pattern.
+-}
+
 -- TODO maybe _chew could pattern the first parameter directly..
 chew :: Pattern Int -> Pattern Int -> ControlPattern  -> ControlPattern
 chew npat ipat pat = innerJoin $ (\n -> _chew n ipat pat) <$> npat
@@ -2002,6 +2282,11 @@
 ascii :: Pattern String -> Pattern Bool
 ascii p = squeezeJoin $ (listToPat . concatMap (__binary 8 . ord)) <$> p
 
+-- | Given a start point and a duration (both specified in cycles), this
+-- generates a control pattern that makes a sound begin at the start
+-- point and last the duration.
+--
+-- @grain s d = 'Sound.Tidal.Params.begin' s # 'Sound.Tidal.Params.end' (s+d)@
 grain :: Pattern Double -> Pattern Double -> ControlPattern
 grain s w = P.begin b # P.end e
   where b = s
diff --git a/src/Sound/Tidal/Version.hs b/src/Sound/Tidal/Version.hs
--- a/src/Sound/Tidal/Version.hs
+++ b/src/Sound/Tidal/Version.hs
@@ -21,7 +21,7 @@
 -}
 
 tidal_version :: String
-tidal_version = "1.9.3"
+tidal_version = "1.9.4"
 
 tidal_status :: IO ()
 tidal_status = tidal_status_string >>= putStrLn 
diff --git a/tidal.cabal b/tidal.cabal
--- a/tidal.cabal
+++ b/tidal.cabal
@@ -1,7 +1,7 @@
 cabal-version:       2.0
 
 name:                tidal
-version:             1.9.3
+version:             1.9.4
 synopsis:            Pattern language for improvised music
 description:         Tidal is a domain specific language for live coding patterns.
 homepage:            http://tidalcycles.org/
@@ -59,11 +59,11 @@
     , text < 2.1
     , parsec >= 3.1.12 && < 3.2
     , network < 3.2
-    , transformers >= 0.5 && < 0.6.1
+    , transformers >= 0.5 && < 0.7
     , bytestring < 0.12
     , clock < 0.9
     , deepseq >= 1.1.0.0
-    , primitive < 0.8
+    , primitive < 0.9
     , random < 1.3
     , exceptions < 0.11
     , mtl >= 2.2
