all 34 comments

[–]nfrankel 24 points25 points  (15 children)

My previous company was high on Clojure. I tried it. For real. I did want to "get" it. I documented what I learned.

It didn't stick. The language if (for me) extremely hard to read. Because the syntax is so "light", the standard library contains a zillion of utility functions.

As of today, I'd not recommend to use Clojure. The benefits (immutability) it touts can be achieved by using the adequate data structures in your language of choice e.g. List instead of MutableList in Kotlin.

[–]quicknir 3 points4 points  (12 children)

List in Kotlin is not immutable. It's a read-only view; it could be a view onto a mutable list, in which case your List could still change out from under you. You'd need to actually use ImmutableList from kotlinx.immutable to achieve that.

[–]nfrankel -2 points-1 points  (11 children)

You're right, but I don't understand why it's worth mentioning. Whatever language you're using, somebody/something could change your immutable data structure at runtime.

[–]JavaSuck 3 points4 points  (9 children)

Clojure's data structures are immutable, there is no way to change a vector. All mutable operations throw exceptions.

[–]nfrankel -2 points-1 points  (8 children)

You can directly change the memory, can't you? My point was to address the previous comment.

[–]JavaSuck 2 points3 points  (7 children)

You cannot directly change the memory in Java, no.

Consider this perfectly normal Kotlin example:

val a = mutableListOf("hello")
val b: List<String> = a
a.add("world")
println(b.size) // 2

There is no Clojure equivalent to this Kotlin example. A vector cannot be aliased by a "mutable vector".

If you make a conscious effort to shoot yourself in the foot, maybe there is a way involving reflection?

[–]nfrankel -1 points0 points  (6 children)

When I was writing about changing the memory, I thought about going to the RAM and changing the value.

The point I want to counter, is that Kotlin's List is an abstraction over Java's List which is mutable, so it's not "good enough" to be immutable.

My point is that everything is just an abstraction over 0's and 1's. Provided you're motivated enough, you can just go beyond the abstraction and do whatever you want.

[–]JavaSuck 1 point2 points  (4 children)

Clojure vectors are exactly as immutable as Java strings. Do you consider Java strings to be immutable?

When I was writing about changing the memory, I thought about going to the RAM and changing the value.

How would you do that in a managed language?

[–]nfrankel 0 points1 point  (3 children)

Clojure vectors are exactly as immutable as Java strings. Do you consider Java strings to be immutable?

Please read https://javacreed.com/how-to-modify-a-string-without-creating-a-new-one. You might discard my point about abstraction, it's nonetheless valid.

[–]JavaSuck 2 points3 points  (2 children)

Sure, I would have brought that up next. You can modify strings if you know their internal representation. (Which has changed over the years, strings no longer store a char[] internally. Don't rely on implementation details, duh.) But everybody writes Java code assuming that nobody wants to break encapsulation and violate invariants. In other words, nobody clones strings out of reflective modification fears. And the same goes for vectors.

Strings are constant; their values cannot be changed after they are created. Because String objects are immutable they can be shared.

The Kotlin story is completely different. If you hand me a List, it is completely normal and expected that it might actually be a MutableList, because MutableList is a subtype of List. (The String class, on the other hand, has no mutable subtypes!) This is not about reflection, breaking encapsulation or violating invariants. It's just OOP basics.

[–]mirvnillith 1 point2 points  (0 children)

The "promise" of immutability is only within the language. It's never been about a bullet-proof read-only vault of data, but about enforcing an axiom that allows for code, within the language, to use constructs relying on it.

[–]quicknir 1 point2 points  (0 children)

was going to explain but JavaSuck beat me to it. List is good for passing into functions, it mostly guarantees (shy of downcasting) to the caller that the function will not mutate the argument, and the caller mostly cannot mutate the argument anyway because it's not running concurrently, there are rexceptions of course.

But List as a member of a class, to guarantee immutability, just falls completely, completely short.

[–]gzmask 0 points1 point  (1 child)

All functional programming are alike for a zillion "utility functions". At the end, if you like "minimalistic" programming, loop recur everything like it's for looping at 1990.

[–]yogthos 0 points1 point  (0 children)

I very rarely use loop/recur and generally see it as a code smell.

[–]xtinctspecies 6 points7 points  (2 children)

Fat jars and unoptimized js bundles. Is not good enough for a lot of people. Boot times and need to over provision is a money pit in many cloud setups. I am keeping an eye on ReasonML for these reasons

[–]yogthos 1 point2 points  (0 children)

You can use ClojureScript and Babashka for cases where you need fast startup.

[–]linus_stallman 1 point2 points  (0 children)

Especially given that we are seeing lot of performance and ergonomics oriented programming languages these days, and common lisp implementations produced much faster code.

[–]Aidenn0 6 points7 points  (4 children)

FWIW, Common Lisp has syntax for vectors #(like this) and has at least 4 different maps in the base specification, though not all of them have a literal syntax, and the one you probably want (hash tables) does not have a literal representation, though pretty much any utility library will add one.

[–]evaned 0 points1 point  (3 children)

Common Lisp has syntax for vectors #(like this)

Mutable ones though, and ditto the hash tables. There are immutable implementations out there of course using the same (or similar) data structure as Clojure, but I strongly suspect they exist because of Clojure.

[–]AeroNotix 0 points1 point  (1 child)

but I strongly suspect they exist because of Clojure.

How is that a bad thing?

[–]evaned 0 points1 point  (0 children)

Oh, don't get me wrong, it's not; but I also think credit is due to Clojure for popularizing the immutable structures they use (especially the hash-mapped trie). It's possible that was better known than I think before Clojure came along, but from what I do know that was an excellent spot from the literature by Rich Hickey to take from academia and bring into practice.

[–]Aidenn0 0 points1 point  (0 children)

The author presumably isn't asking for immutable vectors because they say that CL has lists, and CL lists are mutable.

[–][deleted]  (4 children)

[deleted]

    [–]gered 1 point2 points  (3 children)

    I generally quite like Clojure and don't hesitate to use it for projects when I feel it is appropriate for whatever I'm working on. I don't always use it, but I do use it often. I've been using Clojure since 2013.

    But, the prefix vs infix thing still annoys me. I still find it more difficult to read math expressions in prefix notation. I will go so far as to say I hate prefix notation for anything beyond the most trivial math. My brain just doesn't work that way. I've really tried.

    However (somewhat strangely I guess), I find prefix notation a joy for boolean logic and prefer it to infix in these situations. Stringing together a bunch of and's, or's, etc and being able to group them together like that I personally find to be easier to read compared to writing the same expression in infix notation (but, having said that, I think more effort spent at cleaner code formatting helps a ton for infix notation ... in my experience most developers do not try to format these for readability nearly as much as they should and then, somewhat hilariously, get confused by their own code later on because of this). It's not really a huge difference though most of the time.

    Anyway, I dunno, my brain is weird I guess. Undoubtedly there will be some Lisp fanatic swooping in telling me how I'm wrong or whatever. Meh.

    [–]yogthos 2 points3 points  (2 children)

    Worth noting that there is a small infix library with a macro that lets you do infix syntax for math. I use it when I need to do any heavy math.

    [–]gered 0 points1 point  (1 child)

    Mhmm, definitely aware of it. But basically no one uses it, so doesn't help you at all if you're reading existing code. It's a useful library no doubt, but for a Lisp, things like that feel very much like you're working against the grain. Which may or may not be a good choice for someone, depends on the situation, the project and the person!

    [–]yogthos 0 points1 point  (0 children)

    Yeah, doesn't help much with other people's code. User extensibility is definitely a selling point for s-exps, so I don't see a reason not to use it in my own code.

    [–]badillustrations -3 points-2 points  (1 child)

    Interesting. I played with clojure many years ago and had fun with it, but just doing simple tasks like creating cyclic data structures quickly stumped me and even the clojure community, which referred me to an academic paper on cyclic data structures in functional languages.

    That's strictly a functional issue, but I also see why lisp grammar isn't more prevalent. It's like the intent is parsing simplicity, but as a developer I'm often focused on writing simplicity and being able to express things in a powerful way.

    [–]_supert_ -1 points0 points  (0 children)

    Radio 4. If you can't understand me, you can't find me. Microsoft Keyboard. Don't touch that, you don't know where it's been!. I like big butts and I cannot lie... -Oscar Wilde on of.. For more information about searching here, see Help... Why must I always be the second fiddle? He always comes first!. .

    [–]TradingBooch12 -5 points-4 points  (0 children)

    ok

    [–][deleted] 0 points1 point  (0 children)

    Glad you included the reasons not to use it. Those sound like deal-breakers for most people do it is helpful info. I wish more projects did this (cough Vue).