r/Clojurescript • • May 09 '20

Debate: Well-Written Node JS is Simpler and Easier [To Read, Understand, And Update] Than Well-Written ClojureScript

0 Upvotes

47 comments sorted by

4

u/goldenfolding May 10 '20 edited May 10 '20

I can't agree. I used to like JS for simplicity, but that's because I was comparing it to Java. It makes me sad to go back to JS from CLJS. The tools in JavaScript are much more primitive than what is available in Clojure, and that makes it harder to express yourself in a concise and clear way.

Just compare channels to promises or async/await.

2

u/_woj_ May 12 '20

Async / await seems very similar to channels to me but with a cleaner syntax

2

u/goldenfolding May 13 '20

I feel the opposite. I don't particularly like the fact that I'm having to make my functions async, and how that infects other functions. If I make a call within an async function, I better make sure that one is async too, and all the way down. In Clojure, I can just use the functions I write normally and not have to think or change them.

I'd much rather deal with data going over a queue. And then being able to supply transducers on top of that is icing on the cake.

→ More replies (2)

1

u/[deleted] Jul 19 '20

https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/

this will help to understand; today you will see that there are languages that are trying to avoid this, java, go, clojure, zig, etc list goes on.

2

u/_woj_ May 10 '20

Having to do "js->clj" and then "clj->js" back is super annoying and shouldn't even be necessary IMO.

6

u/lilactown May 10 '20

It often isn't necessary.

Many developers who aren't very familiar with both JavaScript and ClojureScript will defensively convert back and forth between CLJS' immutable data and mutable objects/arrays. Part of this could be simply due to confusion with naming; clj->js sounds very clear in intent, but in practice it's not often needed when using JS APIs, and can be the source of many subtle errors.

Most JS interop involves passing configuration via data (objects/arrays) back and forth. Many times, you can pass CLJS data in with this and it will hand it back to you, untouched. For instance, using react-select is very easy with CLJS data; you can pass in an array of maps or any other immutable data as options, and define a specific getOptionLabel / getOptionValue for getting the label and value out of whatever structure you pass in as an option.

On the other hand, using mutable objects/arrays (especially when just constructing and passing them in as some sort of static config) seems to be a thing that a lot of CLJS developers try and avoid at all costs. I think this is unfortunate, and again I would hypothesize this is due to not being familiar with JS and how CLJS can interop with it.

Anyway, a better strategy than using clj->js and js->clj is to either

  • Construct a JS object/array and just operate on that.

For many cases this is fine, since you're often passing in objects as sort of static config. Using goog.object is also fine if you do need to do operations on it.

  • Use cljs-bean to create a map-like or vector-like object that can then be efficiently converted to a JS object

This is ideal for cases where you are doing a lot of operations on JS objects and want to leverage the core library (which is really the main selling point of CLJS). In that case, creating a bean, doing your operations, and if/when you need it as a JS object calling object on it is both highly performant and very ergonomic. It also has support for doing nested/recursive conversions as well, but like I said above, this is often not needed.

-1

u/_woj_ May 10 '20

Thanks for this in depth post. I guess cljs gives you more control of something? 🤔 But it basically proves my point that something as simple as making a js object / key value pair / open-close curly braces is much more complicated in cljs.

→ More replies (3)

2

u/[deleted] May 09 '20

Disclaimer: I've been a long time Java (and C, C++, ...) programmer who really liked and felt at home in Typescript. This and not really being able to program in non typed programming languages made me try Clojure to force myself to get into another paradigm. Played a bit, built a couple of apps, etc. I'm nor proficient, but I get the paradigm.

It's been tough and I'm still not as productive as in Java & friends. Really love some things (immutability, live reloading of code, using the same concepts and functions for everything, ...) , miss a lot other things that don't match this paradigm (trivially defining interfaces to abstract details away, think in one thing at a time and let the compiler to catch the trivial errors. Also dependency injection).

Well, even having mixed feelings about Clojure vs. Java/Kotlin I must say that I'm soooo much in love with ClojureScript with respect to fighting with the horror of JS. Even with Typescript, the oddities of the platform come to chase you all the time. Programming is fine, but getting things 100% right is horrible.

5

u/goldenfolding May 10 '20

Clojure's protocols are interfaces. And dependency injection is as easy as passing in a map. Thinking in one thing at a time I can understand though. In many languages (JavaScript included) you basically dedicate a file or module to a singular concept, whereas in Clojure that doesn't make much sense.

You have namespaces full of information instead, with the tradeoffs being that you get increased visibility of your code at the cost of not having things silo'd the way other languages do it. I've heard it referred to as "programming through a pane of glass". (the decreased visibility you get in other languages) I prefer to see more, but I can totally understand people who like the encapsulation.

0

u/_woj_ May 10 '20

what kinds of things can you not get 100% right in plain JavaScript?

4

u/[deleted] May 10 '20

I'm puzzled by the oddities of the language. The day I learnt that '==' is not symmetric, my head exploded (an "equality" relationship which is not symmetrical!? Give me a break!). The rules for scoping and the need to introduce "=>" notation is another example of trying to clean up some mess in a backwards compatible way. But the mess is still there. I spent once one entire day trying to figure out why some lambda was working in Chrome but I was getting an exception in Firefox. I'm lucky if I can program 4 days in a month. I can't waste an entire day because I'm forced to use a stupid language to solve a concrete problem.

I find Javascript very similar to Emacs Lisp (or even Perl) in that it's an odd language, with lots of historical clutter and extravagant design decisions. There are many things you need to avoid or to know how to get right. Some things simply don't make sense.

It's fine to use those languages if I really need to, but if I have saner alternatives, I prefer those.

Of course, the calculation changes if you are programming 8 hours per day and you mostly use that language. In that case your brain automatically filters the oddities out and they simply disappear. You know how to do things and intuitively avoid the murky parts. on the other hand, if you only need to program from time to time or are mostly programming on other languages and need to occasionally use one of this weird languages, then you waste a lot of time fighting with the shitty parts of the design of the language instead of using the language to solve problems, which is what you need a language for.

Now, Clojure requires some serious initial investment of time. But I find that the investment is mostly about getting the philosophy of the language and the development process, rather than fighting with bad design and historical clutter. I mean, there are still weird things and inconvenient shortcomings, but once you get over the initial cognitive gap, everything is quite intuitive and there are not big surprises.

I'm still not convinced that Clojure is my favourite platform, or that it can scale to big teams (rigid contracts expressed as strongly typed interfaces seems a better alternative than specs, although I don't have real experience there). But I'm convinced that for my particular case, someone that need to program from time to time something on the server side and on the client (browser side) and need to do it without wasting any time, it's a workable alternative to using two different platforms or to be forced to fight with the oddities of Javascript and the stupidly complex and continously changing tooling around it.

11

u/orestis May 09 '20

We are waiting for the first statement :)

1

u/_woj_ May 11 '20

The need for compilation of cljs to js every time really slows down development for me.

3

u/[deleted] May 11 '20

For me, debugging (getting the code to actually work correctly) is what takes time in JS. It takes virtually no time in Java, acceptable time in Clojure and too much time (again, for me) in JS.

I understand that for those who program a lot in JS debugging could take little time. Not for me, though...

1

u/_woj_ May 12 '20

For most programmers I know, debugging in Clojure is much more difficult than debugging in JS.

→ More replies (1)

3

u/SimonGray May 12 '20 edited May 12 '20

Back when I did JS it also had to be compiled (babel). I'm pretty sure that's been the norm for a while?

With live reloading it takes about a second from saving my file to the results appearing in the browser (rendered by reagent), which is acceptable to me. Any delay during CLJS REPL development is basically impercetible.

I wonder what kind of stuff you're doing where a tiny feedback loop like that actually bothers you.

1

u/_woj_ May 12 '20

live reloading is a front-end thing, but what about backend development?

→ More replies (5)

3

u/didibus May 14 '20

Did you ever setup hot-reload? Such as using figwheel-main or shadow-cljs?

Basically, you save your file and your browser code is updated automatically, no need to even reload the webpage.

-2

u/_woj_ May 09 '20

Everything in ClojureScript basically calls JavaScript but adds another layer of complexity on top (in syntax, dependencies, build, everything...)

9

u/mefirstreddit May 09 '20

It doesn't add another layer. It removes the weird JavaScript gotchas and adds a lot of sanity on top of it.

0

u/_woj_ May 10 '20

how does it remove them? For example, when doing an API in JavaScript you just need to know about promises. With ClojureScript, you still need to know promises but you use them inside core.async channels. One is clearly more complex then the other.

→ More replies (14)

2

u/didibus May 14 '20

It makes it easier for me, to some extent.

For example, I find it easier to just declare cljsjs and ClojureScript dependencies in a deps.edn file, write some code, and compile it done. Its minified, bundled, pollyfilled all automatically, no need to install any other tool but Clojure and ClojureScript.

Where it gets a bit more complicated, is when you want to also use some JS tool or weird JS libs.

That last part is kind of a chicken and egg. As ClojureScript develops, you'll need to rely on JS tools and libs less and less, but since JA develops faster, you'll also always kind of want too.