PiLisp: pl> Macro
PiLisp's REPL wraps everything you submit to it with an invocation of
pl> macro expects expressions to be separated by the pipe
symbol. The forms between pipe separators are wrapped in an implicit
pair of parentheses, meaning that they are invoked, and the return
value of each pipe-delimited expression is threaded as the last
argument to the next expression:
pl> bindings | keys | sort | take 10
The initial form is invoked only if it is a function or a symbol that resolves to one, so that other types of values can be entered directly:
pl> [0 1 2 3 4 5 6 7 8 9] | map (partial * 3) | filter even?
Finally, if you want to have the previous expression's value placed in
a non-final position, you can use the
pl> "hello" | dart/String.toUpperCase | str $ " world!"
A desire—at the REPL—to write programs in this style by default was
part of my initial inspiration for building PiLisp's
sc which had this syntax hard-coded into the language
implementation. One of the most satisfying moments of PiLisp's
development was implementing macro support to the point where I could
pl> as a macro in PiLisp itself.
This syntactic approach of
pl> presents problems when a program
spans multiple lines, but at an interactive REPL, the ability to
append forms and further refine a program by pressing the up key followed
| leverages years of CLI/shell muscle memory and results in a
low-friction experience for small experiments. It also naturally leads
to thoughts about mirroring the system shell in other ways, e.g., by
supporting a notion of "current directory" as Ruby's pry
does. I did this for
sc, allowing one to "step into" a Shortcut
story, epic, iteration, milestone, etc, and PiLisp currently supports
a rudimentary facility for using
cd to "step into" data at the REPL.
In my own Clojure development, I have moved away from using interactive REPLs directly when writing larger programs, preferring to interact with the REPL via code in files. For code bases with multiple files, dependencies, and a lot of different classpath and conceptual contexts to navigate, I find this workflow superior to typing at the Clojure REPL directly.1
But PiLisp is designed for smaller programs. A fast-to-start Lisp environment that provides a CLI REPL by default, with access to the Dart ecosystem when your experiments need to expand, and that can be compiled to a standalone executable by the built-in toolchain2 has filled several gaps for me that Clojure on the JVM alone has not been able to fill.
If the REPL were a more powerful interactive partner, this might not be the case.↩
Babashka for Clojure specifically and native-image on GraalVM for the JVM fill these gaps. Part of the challenge is that these are not part of the standard Java or Clojure toolchains and in the past configuring builds for native executables using these tools has involved build and configuration twiddling that I've found frustrating.↩