Plenty of development environments for non-homoiconic languages provide the ability to evaluate a selected expression (not just the last one) in a linked REPL on demand.
I love the Lisp family, but I don't know why its advocates often sound like they haven't seen a dev environment for a non-Lisp language since the early 1980s when pointing to “unique” advantages of Lisps.
So, yeah, anything people do with Lisp could be done for any other language, it only requires more complex tooling. How much more complexity depending on the language, and it varies from "barely perceptibly more" into "that's completely not practical".
See the book Practical Common Lisp for an intro: https://gigamonkeys.com/book/
Those boundaries you talk of are the crux of the issue. A highly dynamic, completely expression based language is going to enable a much different experience. Homoiconicity also plays an important role here, because you can ispect and parse code within the language, with the same functions and algorithms as everything else.
In Clojure, you literally built your program in memory while you’re writing the source code file, updating the memory representation of your running program part-by-part without ever stopping it.
This experience is absolutely magical, and it’s very hard to go back to edit-compile-run loop in other languages after that. It is completely different from “my language has a REPL and I can execute parts of code in it”.
in particular this does not mean that it is better but the same advantages of being able to put anything into anything that you find in expression languages like rust in lispy languages are also syntactically in editing source code.
since I started using shrink/grow selection in vscode I have much increased interest for simple syntax aware editing, something simple would be moving blocks of code token by token
For example, in a full CL, you can modify a class that is currently being used, and objects of that class will adjust accordingly (for most modifications). This is not possible for the JVM - to modify a class, you would have to unload it, which can only happen if the Class object is garbage collected, which requires all instances of the class to be GCd, and the ClassLoader that loaded that class as well - only then could you add a field with a default value to the class.
Still, personally I have little to no experience with Clojure, so I can't really comment too deeply.
$ sbcl
This is SBCL 2.1.1.52.HEAD.321-f8a57bcca, an
implementation of ANSI Common Lisp.
More information about SBCL is available at
<http://www.sbcl.org/>.
SBCL is free software, provided as is, with absolutely no warranty.
It is mostly in the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution for more information.
\* (\* x x)
debugger invoked on a UNBOUND-VARIABLE in thread
#<THREAD "main thread" RUNNING {1001860103}>:
The variable X is unbound.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE ] Retry using X.
1: [USE-VALUE ] Use specified value.
2: [STORE-VALUE] Set specified value and use it.
3: [ABORT ] Exit debugger, returning to top level.
(SB-INT:SIMPLE-EVAL-IN-LEXENV X #<NULL-LEXENV>)
0] 2 Enter a form to be evaluated: 3
9
\* Debugger entered--Lisp error: (void-variable k)
And then a stack trace. No option for restarts.It works if I run from the command line though..
If you want to run expressions in context just use the expression editor like a normal repl
Something I haven't tried yet is redefining functions at debug time can't see why it wouldn't work though
Or you pull out expressions and test them in isolation with given (assumed) inputs.
REPL are super nice for this!
I've had a hard time in Java, Fortran, etc. in determining the right development patterns that are used in this space to be productive.
Step2, IDE: Right click + Run.
Step2, CLI: $ run <test name>
Bonus: The test is now part of your automated test suite, and will be ran many times to ensure things don't break.
Maybe it takes 3 minutes to get it into the state where it failed, so instead of starting over every change you just want to modify the one function over and over.
Doing it in a repl like jupyter has you run the actual code many times as you're writing it so ensure things don't break before you ever save the file. Of course thats not an excuse to skip tests, but if I was going to pick one, I would choose running the code as I write it, instead of testing it after the fact.
B. Then memoize the expensive function to disk. Bonus: Can keep multiple versions on disk, thus can tweak the expensive function while having fast reloads with guaranteed correct version.
but that depends on how the environment is looking at the time.
during development, in scheme, i just have a function (reset) that reloads all the files of an app. sometimes i create a thread and poll the filesystem and reload everything that changed in the last second. that way i never have to C-x anything. if i need to eval a subexp i just jump on the repl and do it.
In fact they weren't always unresponsive, seems Jetbrain's products have regressed in the last year, but mostly Webstorm. I use WS on OSX and Win10, and on both I get all kinds of slowdowns - different projects too. Tried disabling features, plugins, different GC, increased heaps. I may have to fire up J. Mission control at this point.
IDEA and Clion on Win10 are mostly fine.
It would just be cool to have all of these features, but the frontend be a responsive CLI.
In emacs, for instance, you often use `eval-last-sexp`, by default bound to C-x C-e. This lets you move your cursor to a particular point in the file, often deep in a function, and get the results of just the form(s) you're pointing at.
This is a superpower! It lets you test small pieces of your code without writing any test harnesses or scaffolding. Try it with an editor that embeds the repl and has these commands, and you'll never want to develop any other way.
It does cause you to want to structure your code in 'repl-friendly' ways, so that you can, say, restart your main server or reset your state without restarting the whole process.