3 ms·
When showing off Clojure the application is often molded while running by patching it via the repl. But what I don't understand is if you can actually develop r
by jfries 9y ago
When showing off Clojure the application is often molded while running by patching it via the repl. But what I don't understand is if you can actually develop real programs like that?
Surely even in Clojure code there are lots of dependencies so you can't just change one place in isolation.
And I also guess that changes done on the repl aren't actually saved for the next time you run your app?
And how do you do testing?
How is the actual work flow you use when molding your app?
- falcolas 9y agoThe trick with some of these runtimes (I don't know if Clojure does this, just noting that the paradigm exists) is that the state of the runtime is not lost when going from development to production. The entire state of the program, including the code and the globals in memory, is preserved, and simply spun up in a different environment. This tripped me up for some time with Smalltalk - I didn't understand this. It's the same with many lisps - you can frequently get a REPL directly inside a running program, and query/change the objects (including code) that is running. This was used to great effect in fixing the Deep Space 1 probe while it was 100 million miles away from Earth. https://en.wikipedia.org/wiki/Deep_Space_1 https://en.wikipedia.org/wiki/Deep_Space_1
- dasmoth 9y agoClojure doesn't do the image-based persistence thing. You can ahead-of-time compile to Java byte code if you want, but that's probably closer to the .fasl files created by some Common Lisp implementations. No old bits of state floating around when you deploy a new server. Do occasionally miss the ability to save an image, but it doesn't seem to be the modern way (and would be a nightmare to implement well on top of the JVM). Can easily add a REPL to any Clojure program though.
- fulafel 9y agoRe workflow: A typical one is to sketch something out in the REPL, then paste the code into a suitable function in your module, and live-reload it and test it by calling it from the REPL. You'll also typically define some variables holding your test data in the REPL. Sometimes you skip the sketching out part if you already know what you want to do. I'm not quite sure what you meant by the dependency question. If you need to reference other namespaces, you can add new (require) imports in the repl or live-reloaded source files. If you need to add completely new third party libraries to the project, that's rare enough that a REPL restart is fine - there's a library to do it dynamically at runtime if you want to though.
- pjmlp 9y agoYou can do stuff like send current (line, function, file, ...) to the REPL from the editor. So you can interactively change the code on Emacs, Cursive, CounterClockwise,... and then update the REPL state. While using the REPL for debugging purposes. So at the end of the coding session, all source files have the current state of the application.
- didibus 9y agoBut what I don't understand is if you can actually develop real programs like that? Well yes, I do it daily at my work. We develop backend SOA enterprise services like that, it works great. Surely even in Clojure code there are lots of dependencies so you can't just change one place in isolation. Well, there still are a few, but very little compared to most other languages. That's because everything is immutable by default, and state is passed around instead of accessed globally most of the time. So you'd be surprised how often you can actually work in isolation. Achieving this was Clojure's number one design tenet. To have a language which promotes untangling dependencies as much as possible, that makes it easy to write simple untangled code. The other thing to realize is that the program running is not an isolated one like when doing TDD. It is the full program, with all its dependencies, connected to the file system, your databases, etc. I don't mock anything, if I'm trying to write my DB query, I try it for real on a real database. And I also guess that changes done on the repl aren't actually saved for the next time you run your app? So, that's why Clojure has tight integration between your editor and the REPL. Or tight integration between code files and the REPL. You normally don't type code at the command line, in fact, the Clojure REPL has no UI or command line interface, it's a network repl which listens to messages over a port using a special protocol called nREPL. Each editor that support Clojure connect to it, and send the content of the buffer to it for you. So you're editing the file and saving it as you see fit, while also asking the editor to have the file or part of it as you edit be sent to the REPL. You can also edit the file, save it, and then have the repl watch file changes and auto-reload them as they change. That way can work even with editors that don't have Clojure support. There is a CLI you can use too, for when you want an ephemeral program. Some editor also build UIs for the REPl, allowing rich media to be printed instead, like an interactive graph. And how do you do testing? Well, you're always testing, as you code, in parallel. Since your code is running live as you edit, you see the impact of your change right away. You're expected to also write unit and integ tests, and you do that like in all other language. These are needed for regression testing, but you don't need tests to test something works, you'll be doing that as you code fron the REPL. You need tests to make sure someone in the future doesn't revert your functionality. I actually find these are much easier to write in Clojure, again because the language pushes you to write isolated easy to test code, but also because you don't need a test framework and a mocking library, the core language is enough offers both. How is the actual work flow you use when molding your app? First I create a project. Then I start a repl configured from that project, then I open my editor and connect to my repl. Then I write my main function and load it in the repl. I add more and more functions, loading them and trying them out as I do. Once I've got enough, I orchestrate calls to them from my main method, loading and reloading it all as I go, seeing the result of every step in the REPL. When I'm satisfied, I save my file. Once I've got what I want, i create a test file, write some regression tests, and then git commit, send a CR request, and then git push. Rince and repeat.