16 ms·
Clojure has been there as a fullstack language for a lot longer. It also fulfills the story that Elm was trying to do for much longer. And Reagent is a much nee
by nickbauman 6y ago
Clojure has been there as a fullstack language for a lot longer. It also fulfills the story that Elm was trying to do for much longer. And Reagent is a much needed improvement on React.
The borrow-checker in Rust is kind of silly tool to use in the context of a managed language (that does GC) like Javascript. I don't get it. It's like using a backhoe to plant a few geraniums. Am I just not getting this?
- deleted 6y ago[deleted]
- keithasaurus 6y agoI don't see clojure in the same realm here. Both rust and elm are geared more toward enforcing correctness through their type systems to tame complexity in large projects. In my experience, clojure, being dynamic, fits more as a comparison to vanilla JavaScript. In a langauge-to-language comparison, I think clojure is somewhat more appealing than JavaScript due to immutability by default and general functional niceties. However, the fullstack comparison is not quite apples-to-apples as clojure (backend) is JVM and clojurescript (frontend) is node. While it works for some people, clojure feels awkward to me as a fullstack language.
- nickbauman 6y agoTypes provide only the simplest most trivial kinds of correctness, though. And people routinely make mistakes with types.
- AlisdairO 6y agoThings like null safety aren't trivial for practical purposes IME.
- nickbauman 6y agoMost Optional implementations are kinda terrible, though. They cost more than they save. If I have a function with a param and I release it with it required, but later I change it to be optional (loosening a requirement) does rust require everyone calling that function the old way to change their code? If so that not an improvement!
- steveklabnik 6y agoIf you change a type from T to Option<T>, yes, all the other code has to change to take the option into account.
- nickbauman 6y agoYeah that's not good design. Lowering a requirement should not make callers complying to a stricter one have to change anything. But, ohhh.. right only the type changed. so everybody stop what you're doing and start over.
- steveklabnik 6y agoI strongly disagree, but this is why it's great we have a ton of languages! To me, forcing you to handle it is the exact point of an option type. The type wasn't the only thing that changed, the possible values have changed. You may be getting more values than you were previously. This means that some of your assumptions may be incorrect. Of course your code needs to change.
- skoodge 6y agoI might be misunderstanding, but I think you are talking about slightly different points here. It seems to me that the critique of an explicit Option type (that acts sort of like a box, in contrast to Kotlin's T? vs T) applies to when you pass in the Option as a function parameter to a function that previously expected it to always be T instead of Option<T>. In that case you as a caller are never "getting more values than you were previously", but you can now certainly pass in more values than you could before. Forcing callers to refactor their calls to use a new Option<T> type as a parameter simply amounts to a change in the type signature, but since the function is more liberal than before, it cannot break your assumptions (at least not assumptions based on the function type signature). (For what it's worth, I do find Kotlin's T? to be more elegant than the Haskell/Rust-style Option/Some type. But then again, Kotlin is not fully sound, so there's that. Dart's implementation of T? will be fully sound though, so there are definitely examples of languages going that route.)
- johnsoft 6y agoI think simple, trivial type systems offer simple, trivial correctness. Robust type systems can offer robust correctness (when used to their potential).
- jhomedall 6y agoIn addition to the benefits listed in the other comments, exhaustive checks in sum types are useful for catching unhandled cases, especially when refactoring.
- deleted 6y ago[deleted]
- keithasaurus 6y agoTypes still exist in clojure of course, you just have to spend time reasoning about what they may be in any given place. The longer I've worked with clojure, the less I've understood this argument.
- nickbauman 6y agoWriting your own type system in a Lisp is an afternoon task, but nobody does that because...
- deleted 6y ago[deleted]
- dragonwriter 6y ago> clojure (backend) is JVM and clojurescript (frontend) is node. Node is a backend JS implementation. ClojureScript is compile-to-JS and can run on the backend in Node or on the frontend in browsers.
- keithasaurus 6y agoThanks, that's correct. I often make the mistake of interchanging node for browser js on accident...
- Scarbutt 6y agoClojure is nice for heavy data engineering projects that require robust/stable/mature tech like the JVM. But for a full stack language for a web app it just adds a tons of complexity over just using JS. In Clojurescript interacting with the JS ecosystem is painful cause of its reliance on the closure compiler. In Clojure, is almost the same, most Java libs are over-engineered and horrible to use but you need to reach for them because Clojure lacks an ecosystem. So while Clojure is a better/nicer language than JS, the tradeoffs are not worth it if you want only one language for your webapp (SPA and server).
- danenania 6y agoIt’s been years since I used clojurescript so ymmv, but imo integrating so tightly with the closure compiler was a big mistake. It adds tons of futziness and build complexity for what, in practice, are usually pretty marginal reductions in bundle size that end users won’t notice.
- nickbauman 6y agoNever had a problem with the closure compiler vis-a-vis ClojureScript, is this really a thing?
- StreamBright 6y agoIt is a valid point I don't know why the downvotes. I think Rich had to make a decision, writing a completely new LISP from scratch or leverage an ecosystem and create a LISP on the top. >> In Clojure, is almost the same, most Java libs are over-engineered and horrible to use but you need to reach for them because Clojure lacks an ecosystem. The biggest problem with Java libs the mixed quality. Many big data project have this problem, if you peek under the hood you gonna be amazed about abstractions leaking into different bits and pieces of the system. My favourite example how ORC imports Hadoop FS https://github.com/apache/orc/blob/master/java/core/src/java/org/apache/orc/OrcFile.java#L31 https://github.com/apache/orc/blob/master/java/core/src/java... There is one more problem with Clojure that I find annoying, the actual Java interop. I was running into issues with this many times and some Jave libs are almost unusable without a thin wrapper written in pure Java. Other than that, I think Clojure is still one of the best options out there.
- steveklabnik 6y agoThis is a complicated question, and ends up different for each individual. For me, I don't see the borrow checker as being more silly than GC, just an alternative, and one that speeds up my development process, not slows it down. I am also, of course, incredibly biased. Rust also has many, many features that are not the borrow checker. Some people prefer Rust because of those features, in spite of the borrow checker. https://without.boats/blog/notes-on-a-smaller-rust/ https://without.boats/blog/notes-on-a-smaller-rust/ is also one of my favorite bits of writing on this topic.
- hardwaregeek 6y agoWow...there is really really good advice in that post. I was considering making my side project language into a simpler Rust and that post just gave some great suggestions. Especially since I’m writing the compiler in Rust to WASM.
- conradev 6y agoThat is a great link. The point at the end about making the language embeddable is actually one of the things I love about Rust the most. Rust is aggressively cross platform and I appreciate that a lot. Write a parser in Rust once, run it everywhere. I’d use Swift a lot more (which has some of the features mentioned in the article) if the resulting code wasn’t limited to Apple OSes (the Linux support is crap). Or perhaps Kotlin if it wasn’t limited to the JVM, etc.
- RhodesianHunter 6y agoKotlin isn't limited to the JVM thanks to KotlinJs and Kotlin native
- tekknik 6y agoGolang
- deleted 6y ago[deleted]
- nickbauman 6y ago
- johnsoft 6y ago>The borrow-checker in Rust is kind of silly tool to use in the context of a managed language I suspect a lot people are drawn to Rust more because of its type system and great tooling. I'd be perfectly happy with a version of Rust that swapped out the borrow checker for a GC, but such a language doesn't exist today, and Rust does exist.
- nickbauman 6y agoYeah the borrow checker is an interesting solution to a problem I don't have with a managed language. It's the main "safety" feature people coming to rust are introduced. I'm in agreement about Rust here with a lot: "one does not let one's friends skip leg day" there's a lot more to good language design than memory management. Is it worth it?
- ragnese 6y agoOne of the things I like about non-managed languages is the ability to have true destructors. Releasing resources is awkward at best in Java, et al.
- navaati 6y agoNote that you can have linear types (which is what gives you what you're talking about) in managed languages. You can have them as an extension in Haskell for exemple. Rust is the only mainstream-ish language to really use them, though.
- ragnese 6y agoRust uses a mixture of affine and ... "regular"? types. My understanding is that affine is a looser version of linear because the type doesn't have to be consumed. You can have dtors in non-affine types (types that implement the Copy trait) in Rust as well. I'm really only talking about C++ style destructors. Those don't require linear or affine types. But, I agree, that in a managed language, having a linear type is one way to get predictable destructors to run. Strangely, Swift has deinit{} for its class types (ref-counted), but not for its struct types (value types).
- nilkn 6y agoIn many ways the borrow checker is a tool for enforcing safe mutation of values. As a side effect it happens to prevent entire classes of memory bugs, but it also prevents many bugs that aren't related to memory safety. Historically that side effect was the motivation for designing and implementing it, but in practice it's useful as a general guide for writing rigorous and correct software. As a result, I don't think the borrow checker as silly in this context as you're suggesting.
- gameswithgo 6y agoThere is no javascript in a rust web application.
- louisvgchi 6y agoRust is not running in the context of JavaScript. It's running on WebAssembly, which has no garbage collector.
- pjmlp 6y agoMy CPU also lacks one and yet language runtimes get to implement one. Speaking of which, https://tinygo.org/webassembly/webassembly/ https://tinygo.org/webassembly/webassembly/ https://www.assemblyscript.org/runtime.html https://www.assemblyscript.org/runtime.html http://dpldocs.info/this-week-in-d/Blog.Posted_2020_08_10.html http://dpldocs.info/this-week-in-d/Blog.Posted_2020_08_10.ht...
- TheAceOfHearts 6y agoFor anyone curious about Clojure, I'd recommend checking out the CircleCI frontend [0] for an example of a production ClojureScript app. [0] https://github.com/circleci/frontend https://github.com/circleci/frontend
- nine_k 6y agoOne of the problems with Clojure is the same as with JS: it lacks a static typing system. It lets through a ton of bugs which the compiler normally catches, both trivial and nontrivial. Yes, Typed Clojure addresses this problem somehow, as does Typescript. But Elm, Rust, and the language formerly known as BuckleScript address the problem in a nicer and better-integrated way, to my mind.
- peppery-idiot 6y agoI don't think anyone who uses Clojure in anger would put it on the same level as JavaScript when it comes to letting through a ton of runtime errors. The integrated tooling around editors, clojure.spec and the REPL is very good at curtailing the downsides of dynamic typing.
- tekknik 6y ago> Am I just not getting this? No you are getting what’s happening more than most atm. What’s happening is pretty standard when a new language comes out, junior engineers find every which way to hit a nail with it, claim it’s going to change the world, solve cancer, etc and the some time later things calm down and the junior engineers realize there’s more than one hammer in the world. People are trying to push rust real hard due to some misguided agenda that it’s safer (code is data!!!) and it will calm down once they exhaust themselves
- simias 6y agoA lot of ad-hominem and not a lot of substance in your comment. >due to some misguided agenda that it’s safer (code is data!!!) What does that even mean? Rust is a lot safer than C or C++, although of course here we're talking about effectively replacing JS so it's a different discussion. I would personally love to write front-end code in Rust, not because it's necessarily the most appropriate language for such a task but because I'm familiar with it and can reuse what I already know. Basically the same justification people have used to bring JS outside of the browser.
- tekknik 6y ago> What does that even mean? Turing machines are not safe, code is data and it has no way of telling the difference. For instance, if I were to compile valid rust code, then open the binary in a hex editor and start changing the it rust can / will do nothing to stop it and so long as the code is valid it will run.
- oconnor663 6y agoOne nice thing the borrow checker enables, besides GC stuff: You can guarantee that you never keep a reference to the thing a Mutex is protecting, after you unlock the Mutex.
- oconnor663 6y agoReplying to my own comment, and very much related to that, another is thread safety. In most languages, if I have a type with some method that e.g. increments a member variable, that method is non-thread-safe by default. (Modifying internal data structures can be even worse of course, depending on the language and the data structure.) In Rust, such a type is trivially thread safe by default, because the languages just won't let you modify it from multiple threads without a Mutex or similar. When you want types that can be shared and modified without locks, the author of the type has to take steps to implement this (using atomics, lock-free data structures, etc). That means that there's generally no need to trawl through docs or source code to find out whether a method is thread-safe or not. The type itself knows.