Inline evaluation has been around for a long time, but not every programmer has had the chance to use it. If you haven’t, this is an opportunity to try it.
To this end I’ve coded up a simple editor with some code examples. The
following examples are all editable and executable, however no run
button exists to execute them. You’ll use Control-r
to
RUN/EVALUATE pieces of code that you select from inside the editor
panes below.
To see it in action, place your cursor immediately after the
expression you want to evaluate in the editor below. Then, press
Control-r
(hold the Control
key down while pressing the r
key). For example, if you want to evaluate (+ 1 2 3)
, position the
cursor after the closing parenthesis )
and then press
Control-r
. For brevity, we’ll be using ^r
as shorthand for
Control-r
from now on.
1 ;`-- Place cursor after 1 then hit ^r "Gremlins are cuties!" ; `-- Place cursor after " then hit ^r (+ 1 2 3) ; `-- Place cursor after 1 then hit ^r (+ 1 2 3) ; `-- Place cursor after the ')' then hit ^r (+ 1 (/ 8 2) (* 2 3) ) ; `-------`-` Place cursor after each ')' and then hit ^r (+ 1 (/ 8 2) (* 2 3)) ; `-- Place cursor after ')' and then hit ^r ;`-- Place cursor above this line and hit ^r
When you pressed ^r
, the editor found the closest preceding complete
expression and sent it it to a runtime session (REPL) for
evaluation. The result then appeared right next to your code, exactly
where you were already looking. No context change needed to see the
code run.
You may have noticed that you can evaluate smaller parts of an
expression, like (/ 8 2)
, just as easily as a full expression. You
can also evaluate expressions that span multiple lines.
Lisp languages make it simple to write editor tools that can do this. It is trivial to write an editor plugin to detect an expression delimited by parenthesis.
Sometimes, when I’m uncertain about function names or behavior, instead of searching for them, I simply try evaluating them inline to see if they exist and if they behave the way I expect them to.
For example, I know I created a function that renders HTML into a
div
above the editor, but I can’t remember if it’s called
show-html
or display-html
. Let’s evaluate both of the expressions
below to see which one works:
;; was it show-html or display-html?? (show-html "<h1>It's in a DIV!</h1>") ;; `-- ^r eval here (display-html "<h1>It's in a DIV!</h1>") ;; `-- ^r eval here
With inline evaluation, I can quickly discover which function is defined and if it does what I expect, all without leaving the editor or breaking my flow.
In the next example, let’s apply this same approach to discover what
the look
, move
and reset
functions do.
In all examples that follow, try evaluating each expression with
^r
to see the results.
(look) (move :east) (reset)
Notice how the look
function returns data describing your location
in a text adventure game, while the move
function lets you navigate
through this virtual world.
This provides an opportunity to demonstrate how inline evaluation helps us build and refine code incrementally.
Evaluating (look)
and (move :east)
could work as a spartan interface to the
game, but it would definitely be better to display this data using our
display-html
function. Right? Right??
Now if we examine the data that the look
function returns we can
see that it’s returning a hash map of some sort, with the keys
:desc
, :seen
, :exit
and so on. Each of these keys map to
string descriptions of the state of the game.
Let’s work on formatting the data returned by look
into some HTML
that we can display.
;; First lets see what data is returned (look) ;; I see a :desc key that holds a description (get (look) :desc) ;; OK we have a description let's put that into an HTML string (str "<p>" (get (look) :desc) "</p>") ;; Now let's display that HTML (display-html (str "<p>" (get (look) :desc) "</p>"))
Let’s improve our code by breaking out that paragraph tag into its own function:
;; Evaluate this to define the paragraph function (defn p [content] (str "<p>" content "</p>")) ;; now let's see how it works (p "Hello") (display-html (p (get (look) :desc)))
OK, now we are getting somewhere. But we also have to format the
things that are :seen
in the room:
(look) ;; Let's extract the :seen items (get (look) :seen) ;; Add some context to the raw data (str "You see: " (get (look) :seen)) ;; put it in a paragraph (p (str "You see: " (get (look) :seen))) ;; let's add the :desc and the :seen together (str (p (get (look) :desc)) (p (str "You see: " (get (look) :seen)))) ;; then display it (display-html (str (p (get (look) :desc)) (p (str "You see: " (get (look) :seen)))))
So we’re building up some code to format the the data returned from the
look
function as HTML.
So let’s put this code in a function and start working on the function instead of just composing expressions.
;; so here's our initial format function (defn look-html [data] (str (p (get data :desc)) (p (str "You see: " (get data :seen))))) ;; let's see if it's working (look-html (look)) ;; and finally (display-html (look-html (look)))
OK now we have a look-html
function which we can re-use, but there
is definitely room for improvement. If we look at the data that’s
returned by the look
function you can see that there is also an
:img-path
. Let’s use that to add more visual interest to our game
display.
;; let's build up an expression to format the :img-path as ;; an img tag (get (look) :img-path) (str "<img src='" (get (look) :img-path) "'/>") ;; let's insert the image tag into our look-html function (defn look-html [data] (str ;; v-- added img here --v (str "<img src='" (get data :img-path) "'/>") (p (get data :desc)) (p (str "You see: " (get data :seen))))) ;; let's see if it's working (look-html (look)) ;; and let's take a look! (display-html (look-html (look)))
Not bad at all. OK, now we only have one more piece of information
left to add to our look-html
function. When you evaluate the
(look)
function you will notice an :exits
entry which gives the
player clues about which directions they can move from their current
location.
I think we are at the point where you can add the :exits
info to the
look-html
function.
;; here's the :exits data (get (look) :exits) ;; Add the exits data to the function below: (defn look-html [data] (str (str "<img src='" (get data :img-path) "'/>") (p (get data :desc)) (p (str "You see: " (get data :seen))) (p (str "Exits: " )))) ;; test it out here to see if it's working (look-html (look)) ;; and finally take a look! (display-html (look-html (look)))
Do you want to play a game?
Now that we’ve built our look-html
function, let’s use inline
evaluation to explore the text adventure.
Below are several game interaction functions. Using the inline evaluation, evaluate each one to discover its purpose and effect on the game state. This demonstrates how inline evaluation serves as both a development and exploration tool.
(display-html (look-html (look))) (move :east) (stack) (push :picture) (peek) (pop) (unlock-function :_something?_) (reset)
Don’t be afraid to enhance the UI. Here are some suggestions:
- Create a
looki
function that combines ourdisplay-html
andlook-html
functions:(display-html (look-html (look)))
- Develop a
movi
function that callsmove
and thenlooki
to show the new location - Extend your
look-html
function to display the(stack)
information - your own images, there’s nothing stopping you from creating your own images and using those in the game
And end?
Thanks for taking the time to experience inline evaluation.
As programmers, we often fall into cognitive ruts that constrain our expectations of what the programming process can be. These mental ruts, in turn, limit the potential of what we create. I have observed decisions by language designers, programming architecture advocates, and tool builders that directly ignore or impede this type of interactivity—a situation I find genuinely unfortunate.
It’s hard to get people to try things outside their experience. It’s natural to resist unfamiliar ideas and dismiss them. Many who read this will likely respond by arguing that this kind of interactivity isn’t necessary or useful. However, in my humble opinion, that simply isn’t true.
Yes, these may be toy examples inside a toy editor, but this level of interactivity is very real. Clojure programmers—and others who use similar evaluation capabilities—apply this technique daily in professional settings, from data pipelines powering major retailers to the video streaming services you likely use yourself.
My argument isn’t that Clojure or Lisp is inherently superior to other languages, but that inline evaluation is incredibly valuable and deserves wider adoption across programming environments.
By experiencing it firsthand, you might be inspired to expect more from your programming languages and tools—or even create your own.