Skip to content
This repository was archived by the owner on Jun 19, 2025. It is now read-only.

Conversation

@felixroos
Copy link
Collaborator

@felixroos felixroos commented Mar 18, 2025

mondo-hilite

implements a new kind of notation that combines mini notation with the ability to call functions. some links:

most mini notation features work, but there might still be bugs. would be good to get a first version out in the next release. i'd like to playtest it in nudel.cc then

features

brackets

  • [] => stepcat(...args).setSteps(1)
  • <> => stepcat(...args).pace(1)
  • {} => stepcat(...args)
  • () => function application (see more info below)

infix operators

  • * => fast
  • / => slow
  • ! => extend
  • @ => expand
  • % => pace
  • ? => degradeBy (currently requires right operand)
  • : => tail
  • .. => range
  • | => chooseIn
  • , or $ => stack

quotes

you can use both "" and '' to get a string, where the contents can be anything (except the string delimiter itself). (escape characters currently don't exist)

comments

like in js, you can write line comments starting with "//". block comments do not exist for the time being

function application

  • a b # c d => (c d (a b)) applies to the left side, e.g. s bd # fast 2 => (fast 2 (s bd))
  • (# a b) => (lambda (_) (_ # a b)) returns a lambda that transforms its input, e.g. s bd # sometimes (# crush 4)

removed

  • a b # (c d) => a (c d b) applies to the nearest left element, e.g. s bd # (fast 2) = (s (fast 2 bd))
  • $ => applies function to the right, e.g. fast 2 $ s bd = (fast 2 (s bd))

desugaring

all features above are "syntax sugar" for plain s-expressions. it means the parser will output a syntax tree that can only contain lists or leaf values.

Examples

mondo snippet:

$ note (c2 # euclid <3 6 3> <8 16>) # *2 
  # s "sine" # add (note [0 <12 24>]*2)
  # dec(sine # range .2 2) # room .5
  # lpf(sine/3 # range 120 400)
  # lpenv(rand # range .5 4)
  # lpq(perlin # range 5 12 # * 2)
  # dist 1 # fm 4 # fmh 5.01 # fmdecay <.1 .2>
  # postgain .6 # delay .1 # clip 5

$ s [bd bd bd bd] # bank tr909 # clip .5
  # ply<1 [1 [2 4]]>

$ s oh*4 # press # bank tr909 # speed .8
  # dec (<.02 .05>*2 # add (saw/8 # range 0 1))

strudel equivalent:

$: note("c2(<3 6 3>, <8 16>").fast(2)
.s("sine").add(note("[0 <12 24>]*2"))
.dec(sine.range(.2, 2)).room(.5)
.lpf(sine.slow(3).range(120,400))
.lpenv(rand .range(.5, 4))
.lpq (perlin .range(5, 12) .fast(2))
.dist(1).fm(4).fmh(5.01).fmdecay("<.1 .2>")
.postgain(.6).delay(.1).clip(5)

$: s("bd bd bd bd").bank("tr909").clip(.5)
.ply("<1 [1 [2 4]]>")

$: s("oh*4").press().bank("tr909").speed(.8)
.dec("<.02 .05>*2".add(saw.slow(8).range(0, 1)))
same mondo snippet with minimal number of spaces:
$note c2#(euclid 3#(mul<1 2 1>)<8 16>)#*2 
#s"sine"#add(note[0 <12 24>]*2)
#dec(sine#range.2 2)#room.5
#lpf(sine/3#range 120 400)
#lpenv(rand#range.5 4)
#lpq(perlin#range 5 12#*2)
#dist 1#fm 4#fmh 5.01#fmdecay<.1 .2>
#postgain.6#delay.1#clip 5

$s[bd bd bd bd]#bank tr909#clip.5
#ply<1 [1 [2 4]]>

$s oh*4#press#bank tr909#speed.8
#dec<.02 .05>*2#(add(saw/8#range 0 1))

in the repl, mondo notation has to be enclosed with mondo`` . maybe we can find a solution to switch languages in the future. highlighting also works :)

new packages

  • mondo with parser and evaluator (no ties to strudel)
  • @strudel/mondo ties together mondo and strudel

breaking change: control functions call signature

this PR also comes with a breaking change:
so far, control functions would interpret multiple arguments as a sequence (fastcat). i've changed to to either accept 1 or 2 arguments. when given 2 arguments, the function will do a union. so essentially, the function then behaves like it was chained. (because mondo doesn't use method chaining)

other changes

  • the core package now exports a strudelScope object, containing all the changes made to the global scope. it's the scope that is used for mondo evaluation
  • made arp + arpWith top level functions using register
  • the transpiler now exports registerLanguage, allowing to add a new language that can have highlighting
  • setting s to "-" or "~" in superdough will also result in silence now
  • adds tri, sqr, saw, sin shortcuts for waveforms
  • add sin sqr cos aliases for signals removed

problems

there are some name collisions between variables in the scope and strings:

  • sine (the sound) vs sine (the signal)
  • square (the sound) vs square (the signal)

this means: s sine will not work, because sine will be a signal. instead s 'sine' has to be used atm... how this could be solved:

  • avoiding the problem by adding aliases that don't collide (for some reason i've added aliases on both sides atm :D)
  • doing some kind of type inference.. patterns would need types and the interpreter could then choose the fitting type

things mondo cant do (yet?)

how to write lists?

arrange(
  [4, "<c a f e>(3,8)"],
  [2, "<g a>(5,8)"]
).note()

how to write objects:

samples({ rave: 'rave/AREUREADY.wav' }, 'github:tidalcycles/dirt-samples')

how to access array indices?

note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>").arpWith(haps => haps[2])

what else?

felixroos added 13 commits April 1, 2025 21:49
+ allow passing a scope to MondoRunner.run
+ make def a side effect
+ proper lambda with multiple args + closure
+ add "raw" to parsed pairs
+ implement match + if
+ add more sicp examples
+ support special form for function def (might remove later)
+ support multiple expressions in fn body
+ support lexical scoping
@yaxu
Copy link
Member

yaxu commented May 3, 2025

After discussion on discord, to consider:

  • swapping . for #, seems a bit easier on the eye
  • using _ for a rest/gap instead of - (maybe still as well as ~)

I think that was the main things

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants