Why other languages don't have REPLs like LISP's?

Ya, that’s one thing Common Lisp has too, which the Clojure REPL doesn’t, because Java exceptions don’t know about the REPL, but it be great if it did and simply stopped at the error site and let you inspect things from the REPL. I think there’s a Clojure lib that does that but not sure how well it works.

What I tend to do is click on where the error was thrown and then insert a #break in Cider and re-run. That then drops me into the debugger where the error occurs. It’s not as nice, because I need to re-run the scenario that caused the error, and takes a few extra steps to be in the debugger for it, but it’s pretty nice.

2 Likes

I don’t know. Peppering #break only works for the simple problems… but maybe I’m “holding it wrong”. Usually you have things blow up on some corner case at the hundredth invocation of the function, or on the last element of a collection your map/reducing over.

With C++ you need to build a DEBUG build, but that’s just for the symbols. But in Java-land you don’t seem to ever need that. The stack traces show that it knows the original code location. Unfortunately I don’t know what GDB is doing on the system level to recover from crashes/exceptions and to allow you to resume. My (wild) guess would be that an equivalent solution would need something on the JVM level. I haven’t written any Java in ages - but I’d be surprised no ones ever gotten something like that working before (integrating with Clojure/CIDER etc. is another question though :))

EDIT: Is there a way to GDB-like state on crash all the time? · Discussion #2999 · clojure-emacs/cider · GitHub

And yeah, I should probably use the repl less and eval to buffer/comment more! Thanks for the suggestion

Ya, that can be annoying, when you have to step through a long loop, or if it’s a strange edge case from a heavy side effect set of functions working together.

Sometimes, say you got null pointer exception, I’ll #break when nil? on all the variable the function that threw the null pointer takes just before the function call.fkr example. Then you don’t have to manually step through the full loop and check each iteration what is nil or not.

Most likely, you’d need to be able to hook into the runtime when it unwinds the stack, and like interrupt the unwinding process so it doesn’t even reaches the catch, but literally stops right at the try and let’s you somehow get a REPL in there.

I think instrumenting byte code might be able to work to some extent.

On the Clojure side, I think it would be possible to like wrap everything in a try/catch with some locals capturing, but that would be pretty messy and really slow things down I think. Though if you could do it for one function when instrumenting it as debug that might be an alright solution.

You could also checkout GitHub - IGJoshua/farolero: Thread-safe Common Lisp style conditions and restarts for Clojure(Script) and Babashka. as it has that feature to break into a REPL debugger, but I don’t think it does it in a generic way for like any exception thrown anywhere. I think it be for if you use the job and throw a condition then that could be handled by the debugger.

(Polite) disagreements arise…

Here I beg to differ. The usual Javascript development environment shares exacly a Lisp-like REPL workflow.

Edit. Save. (New code is automatically pushed to the browser or Node.) Play with the app and see that the code did what you wanted it to do (or not). Use the console view in the browser devtools or Node’s REPL if you need to poke and prod your app while it’s running.

Javascript encourages REPL-driven development

IMO this is part of why web technologies make such an excellent target runtime for Clojurescript–the development workflow is essentially the same.

The difference here is that Clojure takes the idea another step farther–letting you send individual forms from your editor directly to the runtime to be evaluated while your application is running.

Consequently, I would vigorously contend that “REPL-driven development” is precisely one of Javascript’s superpowers in exactly the same way as it is one of Clojure’s (or other Lisps’; or Smalltalk’s).

Is this a Clojure weakness? Or is it really just time to rebrand…

The one thing I think Clojure should change is this: I think we should consider rebranding the term “REPL-driven development” to something like “Incremental development inside a live application”. Or maybe (Turn software development) InsideOut. Or maybe FuseCoding (with runtime). Or something…

-Dave Orme (coconutpalm)

1 Like

The one thing I think Clojure should change is this: I think we should consider rebranding the term “REPL-driven development”

I’ve seen “interactive development” suggested for this.

2 Likes

I’m going to talk specifically about ClojureScript + Web Browser using Emacs + CIDER + figwheel, and compare this only to JavaScript development in a Web Browser, so no Node, and I’m not going to generalize across all Lisp* environments, though there will of course be a lot of commonalities.

I’m reasonably sure that the JS development environment does not have the same workflow as Lisp* environments, but I could be greatly mistaken here. Regarding your second paragraph that I’ve quoted, that is almost but not quite the full picture at least as far as my ClojureScript workflow is concerned. There is one key step which is missing and which IMO is the crux of much of what we have been discussing here and which I believe is what people are calling “REPL-driven development” (herein RDD):

Edit > Evaluate > Edit > Evaluate > Edit > Evaluate … Save

In all honesty I have never seen a single talk or read anything about RDD in Clojure/Script. For the first two or so years since discovering these languages, my workflow involved launching figwheel from a terminal, then emacs separately, and I would program in what might be termed the “traditional way”, updating source files in emacs, saving them, and either kicking off a compile manually, or relying on figwheel to hot-reload a given file after saving it.

Then eventually I got to the environment I described in my post above, with REPLs (one for Clojure, one for ClojureScript) running inside of emacs, together with a source buffer. This presented a vast improvement over my previous workflow as I could have everything in one window. But that wasn’t the only advantage it gave me. Up to that point, neither of these workflows by themselves constitute what I believe people are calling RDD.

I myself didn’t really discover it until a little later as I was perusing the CIDER manual trying to see if there were useful features which might improve my workflow. While emacs+CIDER provides a brilliant development environment in its own right (and a big thank you to everyone involved in creating and maintaining CIDER, nREPL and so on over the years), the following features are the ones that really stand out and which I believe enable a large part of RDD:

  • Evaluate top-level form surrounding caret (default C-c C-c)
  • Evaluate individual form before caret (default C-c C-e)
  • Evaluate buffer (default C-c C-k)
  • Evaluate previous form and replace with result (default C-c C-v w)

and many others, including viewing documentation, jumping to vars and so on. I’ve listed these in the order that I find each most useful. The ability to evaluate the top-level form surrounding the caret is hands down the single most useful feature to me as far as evaluation is concerned. Going back to my modification of your original workflow (“Edit. Save …”), this is the one thing I spend the majority of my time doing when developing in ClojureScript. More specifically, I do:

  • Edit a top-level form - usually a definition
  • Evaluate that form - which ends up updating a function or a top-level var
  • test the change - usually in the REPL, either by running a function, or running one or more tests
  • rinse and repeat
  • Save

At other times I use any of the other evaluation commands, probably the most useful one other than evaluate top-level form being evaluate buffer.

Now I’m not entirely sure if this constitutes RDD or not, but what I am reasonably sure about is that the above is something that I’ve never been able to do in any REPL-type environment of any non-Lisp language, and this is due to one of a number of limitations, which have already been discussed in this thread. I could be greatly mistaken here, and maybe things have moved on since I programmed in any of the languages for which I’ve used a REPL (e.g. JS, Python, Ruby, Erlang, Haskell), but the key thing is the ability to evaluate a top-level form within the live running program.

Sure, you can copy and paste code from your source file directly into a JS REPL and do it that way, and maybe there is a plugin which allows you to highlight code and send it to a live running program, but I don’t believe the overall experience will ever be as smooth as it is in Lisp* environments, and there is also a lot of room for errors that way.

Yes JS has a REPL, and so do so many other languages, but the additional evaluation abilities which I’ve described in this post is, at least for me and I’m sure for many others, Clojure/Script’s superpower, and that is something which just isn’t available in JS.

3 Likes

As I have been doing more SPA stuff, I find myself living the browser repl a lot. I can transparently eval forms from a cljs file (without saving and waiting for figwheel to pick them up) and they’ll get eval’d in the browser as per normal clojure repl experience. This is really nice and provides a slick interactive flow. I probably don’t even use half the cider shortcuts you mentioned.

It’s a bit unclear if you haven’t found these talks of if you have chosen not to see them. :smile: So I am going to plug a presentation I did at FuncProg Sweden, even though I run into non-rehearsed problems in the end and panic so … well here goes:

There is also this CalvaTV ClojureScript playlist with some more attempts to describe what you and @joinr are describing:

https://www.youtube.com/playlist?list=PLPb7X_9OOo7pqGdES2e_UwRR_wDTJECwx

Haha, good question! No, I have not deliberately chosen not to see them, I just find it incredibly difficult to sit in front of an instructional video for any length of time without getting restless and feel that I should be doing something more “active” as it were, like programming :wink: I’m reasonably certain that if I go through any one or more of these videos, I’ll probably learn a lot, and at some point I probably well. Cheers for posting them.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.