// HACKER NEWS — CYBERSECURITY
A curmudgeon tries a language server
I write code roughly the same way I did ten years ago11 Well, this was true
when I started drafting this article. I have since adopted robots to do much
more of the actual typing of code, unless it’s a project where (a) I care
about code quality, or (b) I try to learn something.: I make edits in my text
editor, I switch over to a terminal window and I run a command to compile and
execute the code. I look at the result, then switch back to my editor. If I am
unsure of what happens, I either add tracer prints to the code and restart the
program, or I restart the program in a debugger. If I hit an exception that
crashes my program, I fix the problem and then restart it.
I’m envious of what Lisp programmers do. Lisp development typically happens by
typing code directly into the repl to add, remove, and replace parts of the
live, running system.22 In practice a Lisp programmer would probably type the
code into a scratch pad in their editor and then send it to the repl, but it is
as though they typed it directly into the repl.
A consequence of this way of working is that early in a Lisp project, there may
not even be any source code to speak of. Instead, the evolving definition of
the system exists only in the memory image of the running process and nowhere
else. This is so unlike how we normally do development that people have
expressed trouble understanding what that even means. A comparison that might
help is to think of how people treat relational databases in the beginning of a
project. Often, people don’t start out with a version-controlled schema
definition; rather, the evolving schema exists only in the running database.
Sure, at some point when the project matures, the database schema is dumped out
to a file, put under version control, and from that point forward, schema
changes are made through explicit migrations. Similarly, a Common Lisp project
that matures will be dumped from memory image to source code eventually, and
then that source code will go under version control, with patches applied more
carefully to the live system.
But in the early stages? Oh, it’s all evolving live.
I’m not a Lisp developer, so I won’t ever get that experience. I’ll stay
envious. But I also realised I haven’t really tried to get closer to it. Maybe
there are some improvements I can get even in my working language of Haskell.
There are some things we can rule out immediately, and others that seem like
partial wins with not too much effort.
Thus, where a Lisp programmer would incrementally send definitions over to the
repl, we use the combination of ghcid and foreign-store to automatically
recompile and restart our entire program without losing important state. For
many classes of software, these two approaches should lead to similar workflows.
The one thing we still cannot do is evaluate code in the repl of the running
program – because ghcid doesn’t support it. However, I mostly do this to test
that functions work as intended, so what we can do instead is write that code as
an actual unit test case, and have ghcid also run tests when it reloads the
code.
We end up with a process where we still write code in our editor, with the lsp
server giving us information about our code. Instead of playing around in the
repl to try things out, we write our experiments as unit tests. When we save
our changes (tests or application logic), ghcid reloads the code, runs the
tests, and restarts the application in a way that doesn’t lose important state.
We never leave our editor. It’s neat on paper, but I wasn’t sure how it’d work
in practice, so I tried it with a toy project.
At the time of writing this, I was trying to patch up my lack of skill with
differential equations and non-linear dynamics. The book I was
reading33 Differential Equations; Blanchard, Devaney, Hall; Cengage
Learning; 2011 has a pleasant focus on computational methods44 Traditional
first classes in differential equations – including the one I took many years
ago – had a heavy a