https://rigsomelight.com/2025/03/12/inline-eval-adventure.html
* Bruce Hauman
Inline Evaluation Adventure
12 Mar 2025
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 "
It's in a DIV!
")
;; `-- ^r eval here
(display-html "It's in a DIV!
")
;; `-- ^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 "" (get (look) :desc) "
")
;; Now let's display that HTML
(display-html (str "" (get (look) :desc) "
"))
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 "" content "
"))
;; 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 "
")
;; let's insert the image tag into our look-html function
(defn look-html [data]
(str
;; v-- added img here --v
(str "
")
(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 "
")
(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 our display-html and
look-html functions: (display-html (look-html (look)))
* Develop a movi function that calls move and then looki 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.
Bruce Hauman
Programmer
bhauman@gmail.com
About
github.com/bhauman
linkedin
Mastodon
blog feed