packages feed

hsc3-lang-0.7: Help/Pattern/pattern.help.lhs

> import Sound.SC3.Lang.Pattern

* Beginning

| One goal of separating the synthesis engine and
| the language in SC Server is to make it possible
| to explore implementing in other languages the
| concepts expressed in the SuperCollider language
| and class library.  (McCartney, 2000)

Patterns in supercollider language provide
a concise and expressive notation for writing
complex processes.

In a strict language the distinction between
data and process is quite clear.

In non-strict and purely functional languages
ordinary data types may be of indefinite extent.

> let ones = 1 : ones
> in take 5 ones

Since there is no mutation in haskell the
pattern and stream distinction is less 
clear.

> let { a = [1,2,3] ++ a
>     ; b = drop 2 (fmap negate a) }
> in take 5 (zip a b)

However, as was noted in relation to the noise
and related unit generators, a notation for
describing indeterminate structures presents
some interesting questions.

* Patterns are abstract

The type of a pattern is abstract.

> data P a

(P a) is the abstract data type of a pattern 
with elements of type a.

Patterns are constructed, manipulated and destructured 
using the functions provided.

* Patterns are Monoids

> class Monoid a where
>   mempty :: a
>   mappend :: a -> a -> a

Patterns are instances of monoid.  mempty is the
empty pattern, and mappend makes a sequence of two
patterns.

> pempty :: P a
> pappend :: P a -> P a -> P a

* Patterns are Functors

> class Functor f
>     where fmap :: (a -> b) -> f a -> f b

Patterns are an instance of Functor.  fmap applies
a function to each element of a pattern.

> pmap :: (a -> b) -> P a -> P b

* Patterns are Applicative

> class (Functor f) => Applicative f where
>   pure :: a -> f a
>   (<*>) :: f (a -> b) -> f a -> f b

Patterns are instances of Applicative (McBride and
Paterson, 2007).  The pure function lifts a value
into an infinite pattern of itself.  The (<*>)
function applies a pattern of functions to a
pattern of values.

> ppure :: a -> P a
> papply :: P (a -> b) -> P a -> P b

Consider summing two patterns:

> import Control.Applicative

> let { p = pseq [1, 3, 5] 1
>     ; q = pseq [6, 4, 2] 1 }
> in evalP 0 (pure (+) <*> p <*> q)

* Patterns are Monads

> class Monad m where
>     (>>=) :: m a -> (a -> m b) -> m b
>     return :: a -> m a

Patterns are an instance of the Monad class
(Wadler, 1990).  The (>>=) function, pronounced
bind, is the mechanism for processing a monadic
value.  The return function places a value into
the monad, for the pattern case it creates a 
single element pattern.

> pbind :: P x -> (x -> P a) -> P a
> preturn :: a -> P a

The monad instance for Patterns follows the
standard monad instance for lists, for example:

> evalP 0 (pseq [1, 2] 1 >>= \x ->
>          pseq [3, 4, 5] 1 >>= \y ->
>          return (x, y))

which may be written using the haskell do notation
as:

> evalP 0 (do { x <- pseq [1, 2] 1
>             ; y <- pseq [3, 4, 5] 1
>             ; return (x, y) })

denotes the pattern having elements (1,3), (1,4),
(1,5), (2,3), (2,4) and (2,5).

* Patterns are numerical

Patterns are instances of both Num:

> class (Eq a, Show a) => Num a where
>   (+) :: a -> a -> a
>   (*) :: a -> a -> a
>   (-) :: a -> a -> a
>   negate :: a -> a
>   abs :: a -> a
>   signum :: a -> a
>   fromInteger :: Integer -> a

and fractional:

> class (Num a) => Fractional a where
>   (/) :: a -> a -> a
>   recip :: a -> a
>   fromRational :: Rational -> a

Summing two patterns does not require using the
applicative notation above, and the numerical
pattern (return x) can be written as the literal
'x':

> let { p = pseq [1, 3, 5] 1
>     ; q = pseq [6, 4, 2] 1 }
> in evalP 0 (p + q)

The numerical instances are written using the 
applicative functions pure and <*>.

* Intederminacy, Randomness

A pattern may be given by a function from
a random number generator to a duple of
a pattern and a derived random number 
generator.

> prp :: (StdGen -> (P a, StdGen)) -> P a

pfix makes a pattern determinate by seeding 
the random number generator for the pattern.

> type Seed = Int
> pfix :: Seed -> P a -> P a

* Accumulation, Threading

pscan is an accumulator.  It provides a mechanism
for state to be threaded through a pattern.  It can
be used to write a function to remove succesive
duplicates from a pattern, to count the distance
between occurences of an element in a pattern and
so on.

> pscan :: (x -> y -> (x, a)) -> (x -> a) -> x -> P y -> P a

* Continuing

pcontinue provides a mechanism to destructure a
pattern and generate a new pattern based on the
first element and the 'rest' of the pattern.

> pcontinue :: P x -> (x -> P x -> P a) -> P a

The bind instance of monad is written in relation
to pcontinue.

> pbind p f = pcontinue p (\x q -> f x `mappend` pbind q f)

pcontinue can be used to write pfilter the
basic pattern filter, ptail which discards
the front elment of a pattern, and so on.

* Destructuring, folding

A pattern has an ordinary right fold, with the
additional requirement of a seed value for the 
random number generator.

> pfoldr :: Seed -> (a -> b -> b) -> b -> P a -> b

pfoldr is the primitive traversal function for
a pattern.  

Right folding with the list constructor (:) and
the empty list transforms a pattern into a list.

> let p = pser [1, 2, 3] 5 + pseq [0, 10] 3
> in pfoldr 0 (:) [] p

* Extension

The haskell patterns follow the normal haskell
behavior when operating pointwise on sequences of
different length - the longer sequence is
truncated.

The haskell expression:

> zip [1, 2] [3, 4, 5]

describes a list of two elements, being (1, 3) and
(2, 4).

This differs from the ordinary supercollider
language behaviour, where the shorter sequence is
extended in a cycle, so that the expression:

| [[1, 2], [3, 4, 5]].flop

computes a list of three elements, [1, 3], [2, 4]
and [1, 5].

* References

+ C. McBride and R. Paterson.  Applicative
  Programming with Effects.  Journal of Functional
  Programming, 17(4), 2007.

+ P. Wadler.  Comprehending Monads.  In Conference
  on Lisp and Funcional Programming, Nice, France,
  June 1990. ACM.