Hacker News new | ask | show | jobs
by seanmcdirmid 4250 days ago
Is a language environment really live if it does not support interaction or encapsulated state (inside rather than outside of the render loop)? Is it really live if you have to manually refresh your code?

Well, I guess it does fit the requirements of live coding.

1 comments

> supporting interaction or encapsulated state

I don't know what you mean. Could you give an example of this? What would you like to be able to do?

Your render loop looks like:

    while true:
      renderP()
Now, renderP will get executed afresh each time. Assuming no static data, if you want any state at all it must be global to the loop; e.g.

    var state = initValue
    while true:
      renderP(ref state)
Immediate-mode UIs suffer from the same constraint, and really, the author is getting most of their liveness by being immediate.
What about the old let-over-lambda?

  do
    var state = init
    fn renderP()
      ...
    end
  end
Depending on what you wanted the state for, I think it satisfies most of the properties you'd be missing with a persistent, not-in loop state. It wipes itself on refresh, it has a distinct owner, it can be individually refreshed just by re-evaluating everything inside the do-block, etc.
You actually don't want to wipe on refresh; i.e. if you are doing a physics simulation using a standard stepped based integrator. You'd be surprised how many artists want to live code particle simulations.
I've wrangled a crazy way to have your cake and eat it too.

  do
    local state = 0
    function render()
      drawLine(0,5,0, 0,5,50*math.sin(state))
      state = state + math.pi * 0.03
    end
  end
To fiddle with the "state" variable from the outside:

  name,val = debug.getupvalue(render, 1)
  print(name) -- state
  print(val)  -- state's value

  debug.setupvalue(render, 1, 0) -- set state to 0
To find out how many upvalues are available:

  dt = debug.getinfo(render)
  print(dt.nups)
If you want to redefine render without disturbing state, you need to backup state and make a new closure with the new definition of render and the restored state:

  do
    local savedstate = {debug.getupvalue(render,1)}
    local state = savedstate[2]
    function render()
      local h = 5
      drawLine(0,h,0,50 * math.sin(state), h,0)
      state = state + math.pi * 0.06
    end
  end
I don't think its possible (or I don't know how) to redefine a function inside a closure. So just make a new closure and restore its state.
Well, there are many hacks around it; see the immediate-mode programming frameworks like Sol. My own personal take on this problem is to make "refresh" a first-class part of the programming model (rather than something managed by the programmer), and then to define encapsulated state that is preserved on refresh (by the programming model). You can read about it in my paper. (http://research.microsoft.com/apps/pubs/default.aspx?id=2112...)

The trick is to generate stable IDs to represent the state, then you can think of it as a global map:

    var map = new Map()
    
    def render():
      var x = map[0x138293]
      ...
      map[0x13244] = y
This resembles your last solution above. The trick is then automatically reproducing those IDs when render is called again (not to mention tearing down any side effects no longer performed, but that is another kettle of fish).
Thank you for that succinct and immediately useful example of let-over-lambda!