On Sun, 15 Nov 2009, Rohan Drape wrote:

> i mentioned this in the context of DSLs and
> 'non-programmers' since my experience with writing
> instruments (ugen graphs, control graphs etc.) in
> haskell is that the only real 'awkwardness' is having
> to introduce unnecessary bindings and nest alternate
> binding constructs.  as a concrete example consider:
>
>  do { n1 <- brownNoise AR
>     ; n2 <- brownNoise AR
>     ; let { n3 = lpf n2 f * m + a
>           ; n4 = onePole n1 0.99 }
>       in return (rhpf n4 n3 0.03 * g) }

That can be written at least a bit more elegantly:

   do { n1 <- brownNoise AR
      ; n2 <- brownNoise AR
      ; let n3 = lpf n2 f * m + a
            n4 = onePole n1 0.99
      ; return (rhpf n4 n3 0.03 * g) }


Or in Applicative Functor style:

   pure
     (\n1 n2 ->
        let n3 = lpf n2 f * m + a
            n4 = onePole n1 0.99
        in  rhpf n4 n3 0.03 * g)
     <*> brownNoise AR
     <*> brownNoise AR


This way you can handle non-monadic and monadic stuff in two zones. Ok, 
let's demonstrate the point with a noise generator with more parameters. 
In Noise.Monadic you have:

lfNoise0 :: UId m => Rate -> UGen -> m UGen

But you also add a module Noise.Applicative, featuring

lfNoise0 :: UId m => m (Rate -> UGen -> UGen)

This design is however more complicated to implement, since the function 
that lfNoise0 returns can be used with different parameters. That is the 
final UGen's random seed should depend both on the seed provided by 'm', 
the Rate and the UGen. The dependency on the Rate should not be difficult, 
if there should be a dependency at all. The dependency on the UGen is 
however critical. The same signal represented by the input UGen should 
always yield the same seed and different UGens should yield different 
seeds with a high probability.


The above example with low frequency noise then becomes:

   pure
     (\n1 n2 ->
        let n3 = lpf (n2 AR) f * m + a
            n4 = onePole (n1 AR freq) 0.99
        in  rhpf n4 n3 0.03 * g)
     <*> lfNoise0
     <*> brownNoise

Thus, the lambda expression behind 'pure' takes non-functorial, 
non-monadic functions as arguments. They can be feed with applicative 
functions or simple functions. This way it might be easier to extend a 
non-monadic UGen with some monadic/applicative stuff.


You might also choose a continuation passing style, and define in module 
Noise.Cont:

lfNoise0 :: UId m => Rate -> UGen -> (UGen -> m a) -> m a
lfNoise0 rate freq act =
    Noise.Monadic.lfNoise0 rate freq >>= act


   lfNoise0 AR freq $ \n1 ->
   brownNoise AR $ \n2 ->
        let n3 = lpf n2 f * m + a
            n4 = onePole n1 0.99
        in  rhpf n4 n3 0.03 * g


This would be just an abbreviated version of
   Monadic.lfNoise0 AR freq >>= \n1 ->
   Monadic.brownNoise AR >>= \n2 ->
        let n3 = lpf n2 f * m + a
            n4 = onePole n1 0.99
        in  rhpf n4 n3 0.03 * g

_______________________________________________
haskell-art mailing list
[email protected]
http://lists.lurk.org/mailman/listinfo/haskell-art

Reply via email to