<https://news.ycombinator.com/item?id=23811382> gr...
# linking-together
g
https://news.ycombinator.com/item?id=23811382 great post on the lisp REPL’s difference from other REPLs. sidenote: can someone explain to me how lisp programmers go from talking to the REPL to updating the code in their source files? in talks and stuff it always seems to be with copy and paste. is that accurate?
k
Your question has been a long-standing frustration for me with all REPLs. The dominant way I've seen people deal with it is by not typing directly into the REPL, but keeping their queries in a text editor and then sending them to the REPL with a keystroke. Which feels quite barbaric. I'd rather Alt-Tab and run a command on a shell at that point. But I seem to be in the minority there.
j
For me (and many clojure programmers I know). There is no distinction between working at the repl and editing the source code. I build the program in the file and have an active running repl connected to that file/project. In fact, I can even load up multiple projects in one repl and work on changes across them. So if I have some library I depend on I can load up it's code, edit it, evaluate it, and have those changes reflected live in my program. I never do any copy and paste. I am working with a live running program and I can do all sorts of things with that. One example is testing out failure modes and simulating in real time systems going down and coming back up, watching how the system handles it. Another is what I've called interactive integration tests. I have some namespace of code that is meant to be run at the repl. It imports parts of my apps and lets me combine them in various ways to test the live working system, but fully parameterizable. This flow is the main reason I enjoy programming in clojure so much. I get immediate feedback and a much closer connection to my program.
1
❤️ 2
k
I'd love to hear more.
There is no distinction between working at the repl and editing the source code.
Does typing something at the repl add it to the file open in the editor?
testing out failure modes and simulating in real time systems going down and coming back up, watching how the system handles it.
How do you turn the manual test into a reproducible automated one?
j
> Does typing something at the repl add it to the file open in the editor? The opposite I am just typing in the file. I know you mentioned sending them to the repl, that is kind of sort of what is happening. But there isn’t some separate repl window I am sending text to. I am just typing and evaluating code. The repl is a background process. I can pull it up and type in it directly, or see print statements there. But generally, I just see the results of my evaluation in line, or in a bigger pretty print buffer. Here is little gif of that (with lots of typos)
How do you turn the manual test into a reproducible automated one?
Once it is in the shape I want, wrap on assert around it and make sure it is where my test runner picks it up. But many times that isn’t the point of the code. It is for a continually evolving system and I am just trying to understand it more, test the bounds, explore data, etc.
c
Interesting. I would say you aren't really using a repl here, you are evaluating 'in line' (even if what is happening under the covers is that the text is being sent and received via a repl). Perhaps that's the standard way of working, but like @Kartik Agaram, I think I expected you to hop into another repl buffer.
k
But there isn’t some separate repl window I am sending text to. I am just typing and evaluating code. The repl is a background process.
Ah, I see. That is cool. I wouldn't call this a REPL, the REPL is just an implementation detail at this point. But it feels perfectly self-contained and internally consistent without needing a REPL. 👍 👍
j
I am using a repl. But a repl in the lisp sense isn’t a user interface. It is a process. There is read-eval-print-loop. I am doing that just sending the print back to my editor. It also exposes an interface which is what people think of as a repl. But the two are connected you can just go back and forth. (at the end I pretty print, small value here so no real difference, just showing it can do that too)
Just to add a bit more flavor to all of this. My repls are usually left open for as long as my computer is running. At work, I usually have 6-10 repls running in the background of various projects. I will have a repl open for weeks or months at a time. I can often go from scratch to full working project and never kill my repl, just continuing to refine my program. When I was learning some neural network stuff, I had an aws machine with a beefy gpu. I had a remote repl on the machine, would run my training overnight, reconnect to the running repl and see the results in some var. I truly can’t imagine being as productive without this sort of setup. It changes not only how you code, but what you code. I can immediately tell Clojurians who don’t use a decent repl setup from how they structure their code.
1
k
I see what you mean. It sounds really cool, a state I've read about but never experienced, where the program is effectively immortal: http://steve-yegge.blogspot.com/2007/01/pinocchio-problem.html
I can immediately tell Clojurians who don't use a decent repl setup from how they structure their code.
I've encountered something similar from the opposite direction. I always found all the architecture astronomy Java programmers got into to be extremely frustrating -- until I started using an IDE and realized that they were using operations that their tooling made much easier than it was for me in Vim. This is why C++ programmers write longer functions than Java programmers: the C++ grammar is context-sensitive and harder to build an IDE for. Which connects up neatly with another thread this morning, with @Jason Brennan: https://futureofcoding.slack.com/archives/C5T9GPWFL/p1599495354127000?thread_ts=1599070210.092100&amp;cid=C5T9GPWFL
There is read-eval-print-loop. I am doing that just sending the print back to my editor. It also exposes an interface which is what people think of as a repl.
This should seem like pedantic philosophical hairsplitting, but I'm finding it interesting for some reason. The way I see it: * Your environment allows showing values of variables anywhere. * For historical reasons, it does this by using a REPL behind the scenes. * Since there's a REPL, it exposes it to you that you could use. But as you said, you don't normally use the REPL. That says to me you have something better. There's a spatial component here that sounds awesome. While it's technically correct to call it a read-eval-print-loop, I think that understates the value.
j
There are people in the clojure community that take that point to the pedantic level and would say that technically I don't have a repl at all because I am not using the built-in one. I ignore that discussion. What I actually find more interesting is the fact that actually the editor interface and the repl interface are both just interfaces over the process. What is really happening behind the scenes is some separate server process that is running my code and accepting socket connections and sending output to various places. That behind the scenes process is the real repl going on and everything else is just a view on top of it.
j
Quite similar to @Jimmy Miller. If you're interested in further elaboration, I wrote something about my own practices here.
g
wow, this conversation has cleared up a whole lot of long-standing questions i didn’t even realize had been nagging me! is there a beginner, out-of-the-box setup for getting a clojure repl working like that?
s
☝️ Yes, I’d like to learn about that too. And does it have to be a Clojure REPL or can it be any REPL?
j
This is what I followed back in the day. Probably a more up to date one exists somewhere. This is clojure specific. Languages really have to be built with this workflow in mind to work well with it. I've seen someone do it well with Ruby, but I have had many failed attempts at making a js repl have the properties I want. https://www.braveclojure.com/basic-emacs/
💡 3
If you use a different editor let me know what it is and I can see if there is a decent setup for that.
g
i use vim but i’d be interested in any vscode setup (or if someone’s written a blogpost for doing lisp repls with evil mode)
j
Lots of people I know use evil mode. Usually they use it with https://www.spacemacs.org/ Shouldn't be hard to adapt the tutorial I linked but start with spacemacs. I don't know anyone with a decent vim setup. People do it, but they never have the workflow I show above. (And are typically very proud of that fact). For vs code I've heard good things about https://calva.io/
👍 1
k
Maybe the focus on the REPL is not the best way to approach the topic of this thread. It's a technicality, a detail of implementation. The more fundamental distinction is that between a programming language and a programming system. In a programming system, there is a system state that includes code and data, and events that update the system state. A particular important type of event is a code update event, but it's just a special case of an instruction sent to the system for updating its internal state. There is no notion of a "program" that you could write, compile, and run. A REPL is a tool to send events to the system, but there can be others, and much better ones, as discussed above. Emacs is such a programming system by itself, and it also become a popular user interface to other Lisp-bases systems such as Common Lisp or Clojure. The other well-known family of programming systems is Smalltalk, which pioneered better-than-REPL interfaces to programming systems. In contrast, a programming language is designed to write a program, representing a process that runs inside a larger computing system that is considered outside. The process is seen as primarily self-contained, and interaction with the rest of the system is considered exceptional and must be made explicit using relatively cumbersome techniques (I/O, shared memory, etc.). Both points of view have good and bad points, and what I'd really like to see is a hybrid: a programming system that permits the creation of well-defined subsystems with well-defined interfaces that can be the subject of contracts, type-checking, sandboxing, etc.
💯 2
j
@Stefan From my above-linked workflow writeup: "the keybindings I use to perform these operations are the same for every dynamic language I use, so my process is similar from Clojure to Racket to OCaml to Ruby to Python". That said, I strongly prefer using a language whose syntax facilitates easy structural operations. An example of this in the case of Lisp-family languages is that with syntax like
(foo (bar 1) (baz 2))
it's trivial to position the cursor after the sub-expression
(bar 1)
and evaluate just that, allowing one to tease apart a larger piece of code in order to test/understand it. In languages with more complicated syntax that lack clear scope delimiters it's much harder to make a good UX for this.
👍 3
@Garth Goldwater Also, for a very lightweight introduction to this kind of in-place evaluation, you can take a pass through: https://www.maria.cloud/intro
w
Back in my day the Lisp image was a source of truth. When you're done for the day, commit whatever image you're working on back into CSV. Source files were there to reset parts of the image. This worked only slightly better than you might expect what with no clean way to merge images.
g
@Jack Rusher i did the maria cloud tutorial a little while ago—it’s one of the things that makes me want to be directly interacting with that kind of system in my development work @wtaysom this is pretty much what i want lol
👍 1
w
@Garth Goldwater though we don't keep whole images around these days, we do store whole runs for debugging/testing/auditing. Could work a more smoothly, but there is a direct line from bug report to getting to the same screen on your own development environment.
k
This from @Jack Rusher's nitty-gritty details description is great:
I resist anything that involves a context change (changing windows/buffers, copy/pasting more than absolutely needed, etc) – flow states are sacred.
(https://clojureverse.org/t/share-the-nitty-gritty-details-of-your-clojure-workflow/1208/8) Compare https://futureofcoding.slack.com/archives/C5U3SEW6A/p1598741044000700?thread_ts=1598713285.112800&amp;cid=C5U3SEW6A. It almost sounds like the world that CA and his acolytes were trying to bring to pass may already exist?!
❤️ 2