monome / crow

Crow speaks and listens and remembers bits of text. A scriptable USB-CV-II machine

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

asl2 mutable reconfiguration

trentgill opened this issue · comments

commented

in solving #405 i'm realising there is a lack of flexibility in the current syntax for mutable variables. Currently, the entire arithmetic chain is executed, then the result is taken and saved into the mutable object.

-- the example from the docs works ok currently
(mutable(1) - 0.1) % 1 + 0.01 -- gradually decreases from 1 to 0.01

-- but breaks if we want a range substantially away from 0
-- eg. to decrease from 2 to 1, then wrap to 2, we could try:
(mutable(1) - 0.1) % 1 + 1 -- DOESN'T WORK
-- this fails because the final +1 is frozen into the mutable
-- this causes the % wrap fn to trigger next time, and it's not what we want

the solution i see is to change from mutable (noun) to mutate (verb). this way the mutating variables can be unified with dynamic variables, which may optionally be mutated per-step.

-- the mutate function therefore *captures* the modified variable back into the *dyn*
-- dyn should be extended to allow anonymous (unnamed) variables (like current mutable)
mutate( (dyn(1) - 0.1) % 1) + 1

one downside is i believe this introduces a limitation that only 1 dyn object is allowed inside a mutate function call (if there are more than one, it would be undefined which would capture the change).

one option would be to retain mutable so that the variable can be explicitly marked as the element to change. this could be renamed mdyn for brevity. any dyn would be selected as the mutating object, unless an mdyn is encountered.

if we retain mutable then mutate could be optional, defaulting to capture the final state, but i think this is not preferable as some asllib functions perform arithmetic on their arguments (which can be mutable), leading to unintended consequences. greatly preferred to be explicit about what is changing.

//

for now i think we can just use dyn for both cases, and add the mutate function. possibility to add a dyn_mutating (need a better name) variable option which would be promoted to the mutating object in case of more than 1 dyn inside the mutate function.

  1. mutate is not a good name for a global fn as likely to break some userscripts. other options?
  2. is there another way to solve this problem of giving the mutation a 'context' that can be restricted?

reference to ASL2, for any newcomers: #399

oooo, love this sort of problem. i've been really digging dyn and mutable. they feel totally expressive from both code + musical standpoints 💖

tl;dr: i’d ideally just want some straightforward way to tell crow that i want a specific mutable value to stay within a certain range and have crow monitor the mutable values and clamp/wrap ’em, without any of the less-legible stuff.

i mean, honestly, looking at this glazes me over a bit lol: mutate( (dyn(1) - 0.1) % 1) + 1
i think it’s a super nice/tight expression, but i’m always more comfortable with explicit declaration as i’m trying to engage with a new concept. however, the core ideas of dyn and mutable absolutely make sense to me on a fundamental half-code/half-music level — dyn is something that can be swapped out for another value, mutable is something that can have math done to it each time its called. that clicks in a really foundational way for me.

the problem you describe in the example is actually exactly what i was trying to solve the other day, when i realized that a metro/clock could serve as a mutable watcher:

function init()

  starting_rise = 1

  output[1]( loop{ to( dyn{height=10}, (mutable{rise_time = starting_rise}-0.1))
                 , to( 0, dyn{fall_time = 0.2})
                 }
  )

  -- option 1: use a metro to reset the loop
  loop_watcher = metro.init{
    event = function()
        if output[1].mutable.rise_time < 0 then
          output[1].mutable.rise_time = starting_rise
        end
      end
  , time  = 0.001
  , count = -1
  }
  
  loop_watcher:start()

end

here, i know i can swap the height and fall_time at any point because they’re dyn variables, but i can also change the rise_time of my mutable variable with the watcher (by modifying starting_rise like any regular ole variable), so i can stay within a specific range (namely, when the bouncing ball becomes flat, restart the bounce from its peak)

this is obviously stupid large, but i’m sharing it to say that i agree that having a dyn inside of a mutable (which is the core of the mutate proposition) is really compelling + a super musically useful thing — and since i’d use it a lot (HERE’S THE TL;DR: SOURCE), i’d ideally just want some straightforward way to tell crow that i want a specific mutable value to stay within a certain range and have crow monitor the mutable values and clamp/wrap ’em, without any of the less-legible stuff.

commented

some thoughts in this gist: https://gist.github.com/trentgill/19109bca849f5fcf39f2124228087bdd
but @tehn is right that we should write the documentation first, and clarify the intended usage before trying to fix the implementation.