Arc Forumnew | comments | leaders | submitlogin
2 points by rocketnia 268 days ago | link | parent

Tonight I did the same thing for the last of my Arc repos, Penknife.

  $ npm install --global rainbow-js-arc framewarc penknife-lang
  $ rainbow-js-arc init-arc my-arc-host-dir/
  $ framewarc copy-into my-arc-host-dir/lib/framewarc/
  $ penknife-lang copy-into my-arc-host-dir/lib/penknife/
  $ cd my-arc-host-dir/
  $ rainbow-js-arc run-compat -e \
      '(= fwarc-dir* "lib/framewarc/")' \
      '(load:+ fwarc-dir* "loadfirst.arc")' \
      '(= penknife-dir* "lib/penknife/")' \
      '(load:+ penknife-dir* "penknife.arc")' \
      '(pkrepl)' \
      -q
  pk> [+ arc["Hello, "] arc["world!"]]
  "Hello, world!"
Penknife really was just a pile of code before now. I originally wrote it on my commute using an Android phone. Now I've finally gotten around to giving it a readme, a .gitignore, and all that. Whew. :)

There was another language I called Penknife a couple of years later because I thought of it as having the same set of design goals, so technically this one's more like Penknife Mk. I. That's what I think I'll call it.

Something about Penknife Mk. I that I often think back to is its approach to macro hygiene.

Its syntax is primarily string-based, but with the ability for other values to be embedded inside the strings. It has a kind of quasiquotaton that surrounds the quoted section with an object wrapper that the macroexpander recognizes. When the macroexpander expands that expression, it switches over to using the scope at the macro's definition site. Interpolations in the quasiquotation are surrounded with another wrapper that causes the macroexpander to switch back to the caller's scope.

When the macro is defined, its lexical scope is captured and carried on the macro's binding. That way, it's the namespace that holds other namespaces inside it. The syntax trees don't have to hold namespaces; they can just hold paths to traverse the namespace hierarchy.

I still think of this as a nice sweet spot; the code has a context-independent enough identity to be compiled, while the namespaces are mutable enough to allow REPL interaction.

Racket's sets-of-scopes approach to hygiene is mature and supports advanced features like local macros and local definition blocks, but I think of it as kind of sloppy. It spray paints one piece of information over the whole syntax tree just to mark that a variable is in scope. This approach could be handy in cases where variable scopes overlap with each other in non-hierarchical ways -- where sometimes one variable is in scope, sometimes the other, and sometimes both -- but I feel like Penknife's wrapper objects are a much tidier model of the typical hierarchical structure. There've been many moments in my Racket programming when I would have liked Racket to have Penknife Mk. I's approach to hygiene instead.

- Penknife Mk. I on GitHub: https://github.com/rocketnia/penknife

- Penknife Mk. I on GitHub (current snapshot link): https://github.com/rocketnia/penknife/commit/509a7c21d750a24...

- Penknife Mk. I on npm (currently version 0.1.0): https://www.npmjs.com/package/penknife-lang

- The Era repo on GitHub, home of Penknife Mk. II and a few other things: https://github.com/era-platform/era

- The Era repo (current snapshot link): https://github.com/era-platform/era/commit/7f6751cb4d15f8c8c...