Understanding |++| and |**|

This is a 'repost' from the Discord.

1:
why does this :point_down: work?

d1 $ s "superfork" |+| note "7 2" |* note 2

but this :point_down: doesn't?

d1 $ s "superfork" |+| note "7 2" |** note 2

|* and |** are defined here, and they look almost identical in definition:

The error it throws:

Warning: GHCi | <interactive>:1126:6: error:
Warning: GHCi |     * No instance for (Floating ValueMap) arising from a use of |**'
Warning: GHCi |     * In the second argument of `($)', namely
Warning: GHCi |         s "superfork" |+| note "7 2" |** note 2'

I'm also asking for

d1 $ s "super" |++| s "fork"

Which throws:

Warning: GHCi | <interactive>:1132:21: error:
Warning: GHCi |     * Couldn't match type Data.Map.Internal.Map String Value'
Warning: GHCi |                      with [a0]'
Warning: GHCi |       Expected type: Pattern [a0]
Warning: GHCi |         Actual type: ControlPattern
Warning: GHCi |     * In the second argument of (|++|)', namely s "fork"'

2:
like, why does |+| work anywhere on a pattern but |++| and |**| don't seem to work
for example

d1 $ s "superfork*8" # note "[0|7]*4" # legato 0.5 # room 0.5 |+| note "0 12 5" # gain 1.1 |- note "<12 0>/3"

you can straight up |+|, |+, |-, etc in any point of the pattern function
but |++| only works as

d1 $ s ("super*3" |++| "fork*2") # note 3

The reason that |*| and * works is because patterns are defined to be numbers with this kind of thing:

instance Num a => Num (Pattern a) where
  negate      = fmap negate
  (+)         = liftA2 (+)
  (*)         = liftA2 (*)
  fromInteger = pure . fromInteger
  abs         = fmap abs
  signum      = fmap signum

instance Num ValueMap where
  negate      = (applyFIS negate negate id <$>)
  (+)         = Map.unionWith (fNum2 (+) (+))
  (*)         = Map.unionWith (fNum2 (*) (*))
  fromInteger i = Map.singleton "n" $ VI (fromInteger i)
  signum      = (applyFIS signum signum id <$>)
  abs         = (applyFIS abs abs id <$>)

However ** only works on things of class Floating. You can hack it to work with an instance definition:

:set -XTypeSynonymInstances -XFlexibleInstances

instance Floating ValueMap
  where pi = noOv "pi"
        exp _ = noOv "exp"
        log _ = noOv "log"
        sin _ = noOv "sin"
        cos _ = noOv "cos"
        asin _ = noOv "asin"
        acos _ = noOv "acos"
        atan _ = noOv "atan"
        sinh _ = noOv "sinh"
        cosh _ = noOv "cosh"
        asinh _ = noOv "asinh"
        acosh _ = noOv "acosh"
        atanh _ = noOv "atanh"

None of the functions required by the Floating instance are useful to define for ValueMap, the noOv function just fails with an error message.. But by defining it at least |**| et al start working as expected.

1 Like

i need to freshen up on some haskell stuff but i think i got the gist of it; thx alex, helpful as always; btw is there a way to do this with |++| too?

Hm sorry missed that one, and I think it's not really possible to get |++| to work with control patterns.

I ended up finding a workaround for what I wanted to achieve; in the drumMachine func here A huge drum machine library for tidal (71 drum machines)