6 ms·
Rust for Clojurists (2015)
- LandR 6y agoThis is wonderfully written :)
- BenGosub 6y agoIndeed, a fantastic read, even though I am not a Clojure programmer
- dustingetz 6y ago(2015)
- diggan 6y agoNice article in general, but seems to miss the most important point (at least for me) of why Clojure (and similar languages) are way better than anything else today: it's REPL driven development. Definitely more important for me than any of the other "why we love Clojure" points. As long as Rust doesn't offer something similar as the REPL driven development Clojure offers, there is zero reason of preferring Rust (for me) over Clojure for the same tasks. Which, because of the last point ("Rust is not homoiconic") won't happen any time soon. Now, if you need to do embedded system development, Rust might make more sense than Clojure, but it won't replace Clojure for the same tasks today.
- LandR 6y agoRDD is the one big thing that really blows me away in Clojure. I now feel quite frustrated having to work in other languages where I'm missing the tight REPL integration. It's ideal for getting into "flow", iterating on ideas, there's no waiting or downtime that you can get in other languages. I don't know why REPL driven development and Clojure type REPL integration isn't how we all want to do development (assuming as you say it's not embedded or some other niche area). It's just so powerful as a tool for getting immediate feedback and allowing a tight, painless iteration on ideas.
- joe_fishfish 6y agoI've only dabbled in Clojure and REPL-driven development, but is there a way of easily converting a REPL sessions into a suite of tests? Coming from more of test-driven development background, I found that tinkering in the REPL was similar to what I would usually do with unit tests, but without the ability to easily build up a test suite that would prevent regressions in future.
- LandR 6y agoNot that I'm aware off, so I use both. I'll start by writing a bunch of tests and have a test runner that runs all my tests everytime I change my source code and displays the results. It's fast, I have one project that runs ~1400 asserts and on each save it shows the results less than 2 seconds later. THen I write the code in my IDE source editor and use a keyboard shortcut to send it to the REPL as I'm working. I wouldn't ever write code directly in the REPL. By the time I've got it working in the REPL I normally then see that all my tests are passing.
- diggan 6y agoDepends on how you use your REPL. I run the REPL in the background and connect vim (via conjure plugin) to the REPL, so I still type out my code into my editor, into files and buffers, and only save them to disk when I'm happy with the code. So usually I end up mostly living within (comment) blocks around the functions I'm interacting with, and when I'm happy with it, move the code into the correct test namespace, which is a matter of just moving the code around and changing references to namespaces.
- slifin 6y agoFrom my understanding https://github.com/cognitect-labs/transcriptor https://github.com/cognitect-labs/transcriptor does this
- CraigJPerry 6y agoI’m a beginner clojurian but i’ve noticed some devs put a repl folder in their source tree, e.g. stuart sierra’s component project has a dev sub folder https://github.com/stuartsierra/component/tree/master https://github.com/stuartsierra/component/tree/master with repl examples in addition to the tests. I missed a trick in life, i started getting into Clojure in 2020 but about 5 years ago i used to sit 1 row of desks away from Stuart Sierra in Stirling and I don’t think i ever properly spoke to him save for hello or so. It was a funny setup in that operation, i had to fight for my small team to even have office space at the time!
- ragnese 6y agoHave you ever tried playing with OCaml? It's obviously not the same thing as a LISP, but its type system is similar to Rust (Rust was largely inspired by OCaml) and it's (much) more amenable to playing in the REPL.
- diggan 6y agoI have not, as last time I checked, there was no vim plugin for editor integration with the OCaml REPL, but did another search now and found something that seems to make it possible (https://github.com/jpalardy/vim-slime https://github.com/jpalardy/vim-slime) so might test it out at one point. OCaml always looked interesting, but now I could actually try it so thanks for the reminder!
- dunefox 6y agoOcaml more amenable for the REPL than Lisp? You can't be serious. Look at CL with Slime, for example - it's seamless.
- diggan 6y agoNo, they are saying that OCaml is more amenable to REPLs that Rust, which is hard to argue against. They begin their sentence with "obviously not the same thing as a LISP" so I'm sure they are aware that it can't get better than it is with lisps :)
- dunefox 6y agoRight, that makes more sense. I guess my brain didn't parse the sentence correctly.
- eyelidlessness 6y agoI don’t know. I worked in Clojure for several years, and very seldom hit the REPL. I spent most of my exploration time in tests. In TypeScript, I split most of my exploration time between types and tests. Occasionally I hit the Node REPL, or the Chrome console. But doing speculative work in an editor, with all its features, has always felt a lot more productive to me. Maybe that’s why I ended up drifting away from Clojure anyway.
- diggan 6y ago> speculative work in an editor, with all its features, has always felt a lot more productive to me Yeah, I agree with this, which is why I spend all my time in the editor, which is connected to the REPL. I never leave my editor to write stuff manually in the REPL, I simply send the forms from my editor to the REPL to be evaluated. So the best of both worlds :) Recent example of how that workflow looks like: https://vlaaad.github.io/reveal/ https://vlaaad.github.io/reveal/ What's on the left is vlaaad's editor, and the REPL is on the right. Usually I have a similar setup, but the output of the REPL goes straight into my editor instead of in a separate window. One question for you, if you rarely used the REPL, how is your workflow with Clojure unless with a REPL? If you're working on a service, do you restart the process after each change or how to you avoid the REPL when working with Clojure?
- eyelidlessness 6y agoWell I don’t work in Clojure at all anymore. But I guess this isn’t substantially different from how I work. The biggest difference is that I spend most of my time sending whatever speculative work to the compiler (TypeScript) or the test runner. Both of which are run in various forms of watch mode. When I worked in Clojure that’s pretty much how I worked then too. My flow now is almost entirely contained in VSCode, and the biggest change since I switched from Clojure is that I’ve embraced the IDE, static typing and a debugger. I very rarely stray far from real TDD (red -> green -> refactor), and I very very rarely run whatever service I’m working on. The big exception is when I do (web) frontend work, which used to be my bread and butter but has been fairly limited for a few years.
- 6y ago
- creese 6y agoI've found I can get the same tight feedback loop at least for the times where the code will not compile. I use Cargo mode and an after-save hook so that each save runs `cargo check` and, on success, also runs `cargo fmt`. A nice side effect is that I don't need to care about formatting any more. (defun cargo-process-check-sentinel (process event) (when (string= "finished\n" event) (rust-format-buffer))) (defun my-after-save-hook () (when (eq major-mode 'rust-mode) (set-process-sentinel (cargo-process-check) 'cargo-process-check-sentinel))) (add-hook 'after-save-hook #'my-after-save-hook)
- deleted 6y ago[deleted]
- pgt 6y agoHaving built remote monitoring and firmware updating systems for high-current LiFePO4 batteries in Rust for low-level STM32 and ESP32 integrations, Clojure for backend and ClojureScript on the frontend - I can corroborate this. The dynamism of Clojure and the memory safety of Rust are a strong combination, especially for streaming EDN data over websockets to Clojure or ClojureScript frontend. However, neither Rust nor Clojure is the end-game. Eventually all languages will IMO adopt memory lifecycle management (lookahead guarantees) and borrow-checking as language features to do runtime optimization. An interesting language in this regard is Carp, a high-performance statically-typed Lisp implemented in Haskell with borrow-checking: https://github.com/carp-lang/Carp https://github.com/carp-lang/Carp It's a shame they did not opt to follow Clojure syntax more closely, or it would make the transition much easier for the growing Clojure community. Clojure seems to be winning the Lisp language wars. Rust could have be so much more if it used S-expressions or M-expressions. The macro language is an abomination compared to Lisp, but I understand they had to lure the embedded C crowd.
- deleted 6y ago[deleted]
- didibus 6y ago> Eventually all languages will IMO adopt memory lifecycle management (lookahead guarantees) and borrow-checking as language features to do runtime optimization Personally I'd be more interested in CPUs adding dedicated GC chips such as this paper: https://ieeexplore.ieee.org/document/8695831 https://ieeexplore.ieee.org/document/8695831 > It's a shame they did not opt to follow Clojure syntax more closely, or it would make the transition much easier for the growing Clojure community I've been keeping an eye on Carp, but never tried it, at first glance it looked like they used the Clojure syntax, what part do they deviate from?
- timdeve 6y agoLove Carp, the fact that it compiles to C means it can run everywhere like embedded (GBA[1], ATSAMD51[2], esp8266[3]) but is also expressive enough that you could build web apps with it[4]. It's very pre-v1 though so expect breakage and API change. [1] https://github.com/TimDeve/dino/blob/master/main.carp https://github.com/TimDeve/dino/blob/master/main.carp [2] https://github.com/TimDeve/carpgamer/blob/master/main.carp https://github.com/TimDeve/carpgamer/blob/master/main.carp [3] https://github.com/TimDeve/carp-lang-arduino/blob/master/main.carp https://github.com/TimDeve/carp-lang-arduino/blob/master/mai... [4] https://github.com/TimDeve/koi/blob/master/examples/notes-json-api/main.carp https://github.com/TimDeve/koi/blob/master/examples/notes-js...
- usgroup 6y ago“Learning Rust will probably not do much to solve that problem for you. It won't assist you in making the ontological leap from a tired stereotype into something sentient and real. You will remain a replaceable silhouette with no discernible identity. It might even exacerbate the problem.“
- deleted 6y ago[deleted]
- tptacek 6y agoI'd have assumed a primer on Rust for Clojure programmers would spend a lot of time on what a pain closures in Rust (necessarily!) are. :)
- jolux 6y agoI was interested in Rust for the strong type system and borrow checker and such as well as praise from functional programmers I trust but my experience with trying to do FP in Rust was so dreadful that I put it down and stopped learning it. Perhaps I will need it badly enough to push through this someday.
- didibus 6y agoThe thing is Rust isn't functional, in that the coding patterns rely a lot on mutation actually, but the compiler can assert that your mutation will be race free, and that you won't segfault and most likely won't leak memory. So you can say it appeals to functional programmers in the sense that they tend to be wary of mutation, so does Rust in a way. Except where a functional language will turn to immutability, Rust turns to an advanced compiler and linear types to safeguard you against the dangers of mutation. The real benefits are performance, which has always been the achilles heel of functional programmers.
- jolux 6y agoYeah, I quickly discovered it’s not functional and has a much different perspective on mutability, despite clearly being inspired in some aspects by FP. As for performance, I don’t think it’s nearly as large a problem for FP in general as some make it out to be. OCaml and Haskell are typically quite fast, but of course they rely on GC, so they aren’t suitable for environments that are heavily resource constrained or where pauses are unacceptable.
- creese 6y agoRemember you can only have one mutable reference whereas you can have any number of immutable references. The ownership rules incentivize immutability.
- lbj 6y ago"Many people try to compare Rust to Go, but this is flawed. Go is an ancient board game that emphasizes strategy." This cracked me up so hard I can no longer deny being a straight up nerd.
- jnxx 6y agoWhat I also found is that Racket is a very good match to Rust, in a similar style as writing Python and extending it with translated C code where speed is needed. The advantage is that both languages allow for a functional style and Racket is much faster than Python. Racket is, almost like Clojure, highly interactive (though not completely so, it has a conceptual separation of compile time and run time), and on top of that, it can call easily into a C ABI, which Rust provides. Both languages (Rust and Racket) are well-suited for writing pure functions returning immutable results which allows for a nice match in style. Calling across the language boundary is a bit slower than in the Python/C case but it can achieve a decent speed-up. Racket's places allow for parallel execution with almost separate memory images, and it also has green threads. And Racket has batteries such as a GUI toolkit and image data types included which is really nice.
- manishsharan 6y agoDumb question from a curious Clojure guy : how terse is Rust codebase for non-trivial cases?
- speed_spread 6y agoIt can be quite terse for a typed imperative language. Macros help a lot with boilerplate. Type inference also helps a lot but is standard in most imperative languages nowadays, so I wouldn't count it as a terseness advantage anymore. Also, Rust doesn't go full Hindley-Milner as types still have to be declared in function signatures, by design, for readability. Generics are interesting and can save some typing when doing "blanket implementation" of traits, but overall still will count as stuff you wouldn't have to deal with in a more dynamic language. The remaining sore point are the lifetimes annotations. Although most are elided by the compiler by now, if you fall into a pattern where you have to explicitly carry lifetimes around, things get tiresome real quick. At that point, I usually take it as a sign that my code is not as optimal as it could be, and try to rewrite in a way that's less verbose. This is very much compiler-centric approach and actually reflects the whole Rust experience where programming is akin to having a dialectic conversation with rustc, which although quite smart can also be very pragmatic in its ways.