Syntax tips for stitching Tidal functions together

Hi.

You took great care, and length, to describe the situtation. Much appreciated.

You ask about (concrete) syntax (= what you write/see), but a discussion of abstract syntax (= the tree shape that is described by concrete syntax), and static semantics (= types) should be helpful.

This is about expressions built from operators (I am showing a ghci session)

:i ($)
($) :: (a -> b) -> a -> b 	-- Defined in ‘GHC.Base’
infixr 0 $

and

:i (#)
(#) :: Unionable b => Pattern b -> Pattern b -> Pattern b
  	-- Defined in ‘Sound.Tidal.Core’

Since # has no explicit "fixity", the declaration infixl 9 # is assumed (4 Declarations and Bindings)

This "fixity" is important to know who is applied to whom. It contains this information:

  • right associativity (of $): f $ g $ h $ x means f $ (g $ (h $ x)) - if you draw the abstract syntax tree (AST) where $ nodes are binary, you get a list-like tree, hanging to the right
  • left associativity (of #): a # b # c # d means ((a # b) # c) # d - tree hanging to the left
  • precedence (that of # is higher than that of $): f $ a # b means f $ (a # b).

Once we have the AST, we can insert type information at each node.

  • The left child of $ must be a function (of type t1 -> t2, for some types t1, t2), the right child must be of type t1, and the root (where $ is) has type t2. Often, t1 and t2 are patterns. The dynamic semantics of f $ x is "apply f to x"
  • For #, both children must be patterns, with a Unionable event type, and the root has the same pattern type. The semantics of a # b is "combine events of a and b"

The relation between the types you mention is

:i ControlPattern
type ControlPattern :: *
type ControlPattern = Pattern ControlMap
:i ControlMap
type ControlMap :: *
type ControlMap = Data.Map.Internal.Map String Value
:i Value
type Value :: *
data Value
  = VS {svalue :: String, vbus :: Maybe Int}
 | ...

A ControlPattern is what you can send to the back-end (with d1, etc.), but patterns with other event types might be used in construction.


About "extend the already awesome resources that exist with ..."

If someone wants to use the above, go ahead. But -

I'm afraid there's no short-cut here to the classical approach in programming languages:

  • concrete syntax (ASCII representation)
  • abstract syxtax (tree representation)
  • static semantics (types)
  • dynamic semantics (execution, evaluation) <== that's where you hear the music

but you might say I'm "biased" as I am getting paid for researching and teaching these things.

Now music (also, graphics) can be used in introductions to programming: students can start to do something audible/visible/creative immediately, while learning aspects of programming, and of a concrete language, along the way. E.g., Home Page , Haskell School of Music – Euterpea Also, see this recent thread about teaching Haskell [Haskell-cafe] Teaching High-School one-semester FP (using Haskell)

But Tidal's goal is not "learning Haskell", it's "making music". The (hypothetical) claim "you can make music, without worring about Haskell" is risky. It somehow works as long as you stay in the mini language, and have a fixed expression on the outside, but it breaks down as soon as you 1) want to know why it's working, or 2) leave that fixed shape.

It is also risky (by me) preaching the one true Haskell way here - one possible outcome being "we don't need static semantics anyway" and switch (music) programming to Python or Javascript - an error that many universities are currently proudly making... But the fact is that types (type constructors like Pattern, type classes like Applicative) are not just nice for software correctness - they are also essential for effective programming (writing, and maintaining, less code - because that code can be much more expressive). Recent example: Ply and chords - #7 by yaxu

There are lots of instances of this in Tidal's source , but they rarely become visible in performances. Perfectly understandable - there's a difference between changing some number (hardware synthesizer: turning a knob) and re-wiring the innards (soldering iron).

1 Like