Hacker News new | ask | show | jobs
Praxis – A live coding environment based on Lua, Lisp and Forth (github.com)
63 points by createuniverses 4250 days ago
6 comments

If you're interested in this then you should definitely checkout extempore:

https://github.com/digego/extempore

http://benswift.me/extempore-docs/index.html

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.

> 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.
Thank you for that succinct and immediately useful example of let-over-lambda!
So far all of the live coding demos were about graphics. I wonder if it's possible to do something similar with something of a much less interactive nature - say, number crunching, compilers, linkers, etc.
I absolutely think so.

My ideal environment would have the ability to pause at a certain point, saving the state. Then I want to run to a another point (maybe only a few lines to a couple dozen lines down) and see the variable values and results in between.

Basically I want to live code with some defined input to section off parts of the program and still be able to iterate quickly by seeing results every time what I am typing will compile.

I also want to be able to visualize more data than just single values. I want to be able to see results of flat arrays, and write custom visualizers for more complex data structures. I have actually done a lot in this area, essentially by having a window in a separate thread that I can pass closures to (that run openGL functions on their data to draw it). This works incredibly well to let you see your software run. I don't know how I would have been able to write and debug some very difficult programs without it.

The whole thing is a repl with a graphical and audio engine at your disposal to use as you see fit. As well as writing your program iteratively in the "socratic" repl style, you are free to fluidly create whatever visualizations you wish along the way to help you. As well as this, if you split the process you have implemented into frames (a simple way is with coroutines or closures) you can make whatever visualization you prefer of your number crunching. For example, throw in some yield() calls in your program and every update() do a coroutine.resume(). In render() visualize the state of your program how you wish. The great thing about this is, as you change your program, the effect of your change will be reflected in what is rendered.
Interesting, it's something to think on. Creating such visualisations manually may be prohibitively complicated, but I can imagine some cases where they can be derived automatically (e.g., for compiler passes, an example IR can be displayed before and after).
You might also want to check out live programming, which focuses on interactive debugging rather than the improvised problem solving (as well as live performance) of live coding. Once you get to live programming, techniques like time travel become viable, which otherwise don't make much sense with live coding. You can then better tackle traditional programming problems with better live feedback.
More often than not, these interactive / interpreted environments feel like Smalltalk by installment.
I certainly had Smalltalk and Lisp Machine environments in mind when I made this, the central idea being to be able to query and manipulate every part of the system while its running, to be able to fix faults and continue.
This is actually a law:

Whenever someone brings up any kind of interactive programming environment, no matter how much it does not resembles Smalltalk or Lisp, someone will always say it completely resembles Smalltalk or Lisp with no significant improvement. We call it the "smug smalltalk/lisp weenie" syndrome.

BTW, Smalltalk supports hotswapping without the liveness of course. There is nothing in smalltalk that allows the code to re-execute in its proper context without execution coming back to the code in a refresh loop setup by the user.

No smugness intended, sorry. I only ever toyed with it. I am genuinely curious why e.g. Squeak never caught on with the artsy types considering it has many but by no means all of the interactivty and feedback that is desirable in these spaces. And again Squeak at least tried to position itself as a media-rich system. Perhaps performance always has primacy. And I don't seem to follow your point regarding liveness -- a language that supports many aspects of live programming but requires a supporting loop is of no value? That does seem like a very strong criticism.
Squeak supports two kinds of liveness. The first is general hotswapping, which appears in mainstream languages (though not done as well as smalltalk), and as mentioned, isn't very live. Once you add the loop, you still have lots of problems to solve (e.g. is their encapsulated state, how is the state upgraded on a code change?). To make it live requires huge compromises, and anyways you could make the same modification with any language that supports hotswapping.

The second is direct object manipulation via morphic, which is quite different from manipulating the program via its code. The line is blurred a bit given that Squeak is image based (so changes to objects that you make will persist).

praxis makes me think of the dutch diy store chain - if you go to one in amsterdam they play reggae and you can buy a bhudda for your garden (or balcony more likely)