5 ms·
Agree whole heartedly! That's why my favorite language is Clojure. That interactivity, instant feedback, seeing the program running as you are tweeking it, its
by didibus 9y ago
Agree whole heartedly!
That's why my favorite language is Clojure. That interactivity, instant feedback, seeing the program running as you are tweeking it, its a bliss to use and it creates better more functional software.
Imagine playing music as you hear it when trying to come up with a good melody. Now imagine not playing it, but composing it on music sheets instead, and occasionaly playing what you've got every 10 to 30 minutes.
Lisp championed interactivity, it invented dynamic programming for that sole purpose. The idea is that you morph a running program into shape, molding it like you would clay.
The first thing you do when writing in a Lisp like Clojure is run your program. In most other languages, running your program happens much later, and much less frequently, and it can actually be quite challenging to run.
- DonbunEf7 9y agoWell, actually, let's explore the metaphor. Here's how I compose music. First, I use a highly-flexible prototyping instrument to quickly iterate on melodic ideas. Normally this is either humming or whistling. "Our first instrument," as an instructor used to call it. I do this until the melody is catchy enough to start sticking in my mind. Then I'll look at alternate parts, like countermelodies, B/C sections, harmonies, or basslines. Each part gets repeated until it gets stuck in my head. Then, I repeatedly hum/whistle each part like a disturbed rambler until I get home or to some other place where I can scribble the parts down onto paper. I used to be faster just typing out the Lilypond, but I'm out of practice with that whereas all musicians generally never stop having a short shitty hard-to-read staff-based shorthand. Now, finally, I actually start playing the different parts on actual instruments, figuring out stuff like fingering for guitar or piano, rhythm and phrasing, filling out transition chords, etc. During this process, I play less and less of the song, drilling down to tiny fragments and setting up tiny local contexts for testing ornaments, phrasings, hits, etc. If I'm gonna try to make the metaphor rigorous, there would be only one language, because the musical process happens entirely in one language. This language is small enough to write on a single piece of paper, fully contains all of its abstractions, has focused notation for specific instruments, and permits debugging any part of a program by cutting any contiguous subprogram out and turning that fragment into its own live environment. I'd write a program first on a prototyping platform which is so lightweight that nearly any prototype program will run, and I'd use that to write out the entire first draft of my program. Then, I'd incrementally move pieces of my program transparently onto a more rigorously-precise framework which is more restrictive about types but makes it easier to compose modules and let them stay composed. There is a point in time when composition is more about managing multiple parts at once and you won't actually want to listen to more than about 15s of your song at once, and you'll want your runtime to support your quest towards stability as a basis for tweaking the fine details of your song.
- didibus 9y agoHonestly, that describes exactly my programming workflow when working in Clojure. The whole process happens in Clojure. This language is small enough to write on a single piece of paper Check, Clojure is one of the most concise language out there. I use it for hand writing code or when coding on my phone, because its so short. fully contains all of its abstractions Yup. In fact, you need to change your mindset, you don't write instructions in Clojure, you search for the functions that do what you want and arrange them in the order you need. Think micro-library. has focused notation for specific instruments Lisps like Clojure are the kings of DSLs. This is literally their bread and butter. and permits debugging any part of a program by cutting any contiguous subprogram out and turning that fragment into its own live environment. Yes, that's what the REPL lets you do. Load any subset of the program into a live running environment. I'd write a program first on a prototyping platform which is so lightweight that nearly any prototype program will run, and I'd use that to write out the entire first draft of my program. This is the idea behind dynamic programming which was invented by Lisp. Clojure will run almost any code, correct or not, no question asked. It does not put constrain on you, though it highly suggests the use of safe tools over unsage ones, it lets you cut yourself if you want too. Then, I'd incrementally move pieces of my program transparently onto a more rigorously-precise framework which is more restrictive about types but makes it easier to compose modules and let them stay composed. This is my ideal too. There's not yet the golden graal for this, but Clojure is currently focused on this very last part. Composing modules is actually pretty trivial, because it supports performant immutable datastructures as first class, open polymorphism, defaults to functionally pure constructs and wraps all state in safe managed containers. Now as for correctness checks, it has optional types, but the implementation is a work-in-progress, and is stagnating a bit. Optional generative tests is the current strategy being explored, as well as highly powerful runtime checks. Still in alpha though. Static analysis is also being worked on, though soundness is not a target, also in alpha at this point. If its any consolation though, the only two studies I could find about defect rates relating to programming language choice showed that Clojure does as well as Hakell. Which means it had some of the lowest defect rates of the languages tested, averaging a tad behind Haskell, and doing better then Scala and F#. Obviously besting Java, C++, Python, Ruby, C#, Go, etc. It was an outlier in that sense, as it was the only non staticly typed check language to do so well.
- jfries 9y agoWhen 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.
- nwatson 9y agoWith a good IDE with an integrated debugger Python and its ilk, and even Java, are like clay. PyCharm, IntelliJ, and their siblings.
- didibus 9y agoMore like cement. I don't mean to be dismissive, but REPLs aren't created equal. I've used good IDEs with Python and Java and C#. The C# debugger is probably the best, but its really not the same thing. Python could probably embrace it more so, and if you've used Jupyter, you got a pretty good taste of what it means to mold a running program, now imagine doing that for all programs, not just data science reports.
- MrBuddyCasino 9y agoIs there a way to write the changes in the REPL back to source file, or do I have to copy? I am wondering since trying out Clojure.
- grzm 9y agoWith a set-up like Cider in Emacs (and I wouldn't be surprised if Cursive with IntelliJ has something similar), you can write code in a file and send it to the repl for evaluation.[0] This is a common method of development which accomplishes what you describe: code in a source file, evaluation in the repl. [0]: https://cider.readthedocs.io/en/latest/interactive_programming/ https://cider.readthedocs.io/en/latest/interactive_programmi...
- MrBuddyCasino 9y agoThanks, that makes sense (also to cormacrelf).
- cormacrelf 9y agoA better pattern is to use your editor as the REPL via SLIME or vim-fireplace or similar. Basically: 1. Headless server like nREPL for Clojure 2. Code editor that can send chunks of text to the headless repl and receive the output. Emacs and neovim (with its terminal) are pretty good at this. You can use commands to send the surrounding s-expression, paragraph, etc. 3. Ta da! Your text is already in your source file.
- Scarbutt 9y agoTo be fair, in Clojure you have to reach for horrible external hacks like Component and Mount to have a "real" interactive development.
- grzm 9y agoWhat would you describe as "real" interactive development? And while the reloaded[0] workflow is one benefit of using a library like Component or Mount, it's hardly the only benefit. [0]: http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-reloaded http://thinkrelevance.com/blog/2013/06/04/clojure-workflow-r...
- didibus 9y agoNot really, I often don't need them. They're mostly for being able to reset the full app state back to how it is at startup. You can also just restart the REPL, though some people find waiting 5 second for that too annoying. I wish the restart times were much faster, but its not as big a problem as most people make it sound like. Often time, you can just learn to mold your program in smarter ways, so that you don't get yourself in weird inconsistent states, or you learn how to fix your state instead of starting over. Or you use mount, components. Integrant, etc. They're really low overhead, mount is trivial to add, its like 4 more characters per global variable. Having said that, Clojure isn't the best thing ever, just the most interactive language I know of currently that is mature enough and has a large enough ecosystem I can use it for commercial software. Do you have other recommendations for a language that meets these criterias?
- Scarbutt 9y agoYou can also just restart the REPL, though some people find waiting 5 second for that too annoying. That's true only if clojure is your only dependency, in real project with libraries is more like 15 seconds with lein repl, cider is much worse. Do you have other recommendations for a language that meets these criterias? Some would say Common Lisp.
- didibus 9y ago