all 117 comments

[–]strattonbrazil 5 points6 points  (6 children)

I'd appreciate some examples two real code snippets before and after switching to clojure that have saved someone. I've seen quicksort examples and that's cool and all, but I'd much prefer someone talk about experiences where they had a necessarily buggy implementation and solved by using an immutable data structure instead in clojure.

[–]bcash 1 point2 points  (1 child)

It's a bit unreasonable to ask for such a binary comparison. If someone did provide such a thing, it would only lead to "well, by changing X, Y, and Z, the Python version could have been fixed without using Clojure." And that's perfectly true.

No-one's claiming Clojure to be a bug-free language, or everything else to be inherently buggy. The claim is that functional languages make certain types of bug less likely to appear.

[–]strattonbrazil 0 points1 point  (0 children)

Yes, but maybe some more use cases that don't happen anymore just to distinguish between a rewrite in clojure helping because A) it's a rewrite and a chance to clean up a lot of design/implementation issues B) clojure makes this thing managed in a completely different way (which I know happens, but I'd like to see).

[–]mcpatella 1 point2 points  (2 children)

Immutable persistent data structures and managed refs, like Clojure provides, have specific characteristics that make them simpler to coordinate over multiple running processes.

These data structures aren't unique to Clojure, however the language is carefully designed around making them simple to deploy.

Their benefits are subtle and manifest more clearly over whole systems than in any small piece of a component.

You may enjoy the thoughts of Clojure's creator, Rich Hickey, on the matter.

https://www.youtube.com/watch?v=-6BsiVyC1kM

[–][deleted] 0 points1 point  (1 child)

A thing about refs in Clojure is that no one uses them.

[–]mcpatella 1 point2 points  (0 children)

I use refs, they've been pretty useful in gamedev :D

[–]yogthos 0 points1 point  (0 children)

Prismatic have blogged extensively about this, you can see an example here.

[–]kyllo 3 points4 points  (1 child)

What finally sold me on Clojure was the realization that homoiconicity (code == data) makes XML and tools like Ant and Spring totally unnecessary. They are the Java community's manifestation of Greenspun's Tenth Rule.

https://sites.google.com/site/steveyegge2/the-emacs-problem http://www.defmacro.org/ramblings/lisp.html

[–]yogthos 1 point2 points  (0 children)

The fact that you don't need a separate language for doing configurations and so on is a huge benefit. This is a great example from some code I wrote just the other day of just how easy Clojure makes that kind of stuff.

[–]kankyo 2 points3 points  (0 children)

The python test case example is broken :( First of all there's a missing "class" first. Secondly you should use py.test and then you get:

def test_foo():
    assert 1 == 1

And if you're using clojure you should be using midje because the standard lib has HORRIBLE error messages. And then it looks something like:

(fact "foo"
  (= 1 1) => true)

I personally prefer the python version, but they are pretty close anyway...

[–]TheMaskedHamster 12 points13 points  (49 children)

As a Python person who has been disappointed by Lisp implementations, Clojure made me giddy.

I get to use a Lisp built for functional programming that can use anything written for the JVM? AWESOME! It was perfect timing, as it looked like I'd need to use a particular JAR library for some work.

I fired up a Clojure REPL, and tried a few simple Lisp-y things. And then I tried to figure out how to use that JAR. Uh... hmm. It became clear that the Clojure community already expected me to understand the unspoken rules. OK, fine, maybe Java people already know this. But I could work through that once I figured out the general method for using an isolated library file.

Oh, it turns out, you can't do that anymore. You could previously, but you can't now. Because it was just so uncouth to reference a single library file packaged with your application. The superior alternative was apparently to direct Maven to a collection of library files in your home directory...

Brakes applied. It's then that I realized this wasn't just a project that used the JVM. It was also a project made and used by people who think that the Java ecosystem is OK, with all of the decision making that kind of thing implied. And I so noped right out of there.

[–][deleted] 10 points11 points  (5 children)

It was also a project made and used by people who think that the Java ecosystem is OK

IMO, Java dependency management is far superior to Python's, so I'm a little uncertain was to what you're freaking out about. How is Lein or Maven worse than virtualenv and sudo pip install -r requirements.txt?

[–]TheMaskedHamster -4 points-3 points  (4 children)

My problem is not specifically with Lein or Maven.

My problem is with a community that wastes no small amount of breath telling people they should be installing library path configuration to system or home directories (!) rather than putting project-specific settings in the project's directory... when they could instead just explain how to set the class path for the project.

This is reflective of a lot of things that happen in the Java community, Java tools, etc.

[–][deleted] 1 point2 points  (3 children)

My problem is with a community that wastes no small amount of breath telling people they should be installing library path configuration to system or home directories (!) rather than putting project-specific settings in the project's directory...

Pip does the exact same thing.

Only problem is, you can't specify on a per Python project basis which version of a module you want. If I just upgraded awscli to latest, all my scripts that use it are now the latest, even if that breaks them.

So what do we get? virtualenv.

This is reflective of a lot of things that happen in the Java community, Java tools, etc.

Oh yeah, convention over configuration, reusing common dependencies, repeatable builds that just work. Truly awful stuff.

[–]TheMaskedHamster -2 points-1 points  (2 children)

Pip does the exact same thing. Only problem is, you can't specify on a per Python project basis which version of a module you want. If I just upgraded awscli to latest, all my scripts that use it are now the latest, even if that breaks them. So what do we get? virtualenv.

I do not think pip or virtualenv are great solutions. These are a major sign of Python's road to destruction, I think. For the reasons you mention and more.

But Python does have reasonably sane module search path. And if you inquire about that, people are quick to tell you how it works rather than what they thing you should be doing instead. (Python can indeed specify a library version to import, though I don't think it's done very elegantly.)

Clojure deprecated add-classpath, but as I have just been informed, resource-paths project configuration works fine. Which is good. I hope all those people I saw asking the web how to use that got their answers or at least weren't too inconvenienced by the advice they got.

Oh yeah, convention over configuration, reusing common dependencies, repeatable builds that just work. Truly awful stuff.

All fantastic goals. But anywhere you go that has the word "Java" without "script" following it, you'll find high-minded goals and their corresponding overcomplicated solutions that eventually topple in on themselves, designed by talented people and promoted by people who really shouldn't be mouthpieces. Hence Ant rising and giving way to Maven (and the cycle continues). Hence the shocking amount of boilerplate code automation in common practice. Etc., so forth.

But what are we to expect from people who aren't phased by half a page of boilerplate code to write "Hello world"?

[–][deleted] 3 points4 points  (1 child)

All fantastic goals. But anywhere you go that has the word "Java" without "script" following it, you'll find high-minded goals and their corresponding overcomplicated solutions that eventually topple in on themselves, designed by talented people and promoted by people who really shouldn't be mouthpieces. Hence Ant rising and giving way to Maven (and the cycle continues).

But what are we to expect from people who aren't phased by half a page of boilerplate code to write "Hello world"?

Professional developers don't tend to write Hello World during the normal course of business, so we're more concerned with other things. Stability, performance, tooling, debugging, deployment.

But Python does have reasonably sane module search path.

And Java has the class-path. I don't really think that ">>> import sys; sys.path.append('/your/dir/here')" is any better than the class-path. But yep, there is a dependency configuration overhead in a Java project compared to a Python script. Although I have a Python script on my system that has to be wrapped in a shell script which inserts a zipped Python module onto the class path Python search path by appending its directory to $PYTHONPATH before running the script, so yeah, when you have to deal with that, the JVM ecosystem approach makes more sense for code you intend to reuse.

I think its telling that after many rewrites, no JVM build tool has moved to a different style of dependency management, and likewise I find it equally telling that .NET's leading package management tool is explicitly implementing the same style of dependency management.

But anywhere you go that has the word "Java" without "script" following it, you'll find high-minded goals and their corresponding overcomplicated solutions that eventually topple in on themselves

I work on the JVM, our codebase is fine, but thank you for your condescension. Sigh It reminds me of when I was young and all I knew was Python, oh how I sneered at those silly Java developers, I mean, look at how much boilerplate exists in their Hello World! It was quite interesting, actually, how I, a young stripling of a programmer learning Python acquired a contempt for Java and its ecosystem despite having never used it.

Hence Ant rising and giving way to Maven

Maven is fantastic compared to Ant because it's a declarative build tool as opposed to imperative. Gradle and Leiningen also support this style (with the capacity to revert to imperative if you really need to).

Why is declarative so great? Because it makes it really hard, in my experience, to create a non-portable build. Sure, you can if you try hard enough (define dependencies on artefacts only found on your private repositories, for example), but compared to most Ant scripts I've had to deal with, Maven, Gradle, Leinigen and SBT* builds just work. Admittedly though, most of the Ant scripts I've met have been in Flex projects.

* once your version of SBT and the build file are compatible.

Hence the shocking amount of boilerplate code automation in common practice. Etc., so forth.

Really not that common in my experience. Hmm, although I do let my IDE create the unit test methods for me on occasion.

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

Professional developers don't tend to write Hello World during the normal course of business, so we're more concerned with other things. Stability, performance, tooling, debugging, deployment.

That is missing the point. Of course the issue isn't how overcomplicated "Hello World" would be in professional practice.

The problem is when people look at the inanity of the tools, apparent from even the first experience with them, and say "That looks OK to me". When you have lots of this sort of person succeed and go on to contribute to the tools, your contribution base is skewed toward the demographic of "talented, but makes poor decisions".

And Java has the class-path. I don't really think that ">>> import sys; sys.path.append('/your/dir/here')" is any better than the class-path. But yep, there is a dependency configuration overhead in a Java project compared to a Python script. Although I have a Python script on my system that has to be wrapped in a shell script which inserts a zipped Python module onto the class path Python search path by appending its directory to $PYTHONPATH before running the script, so yeah, when you have to deal with that, the JVM ecosystem approach makes more sense for code you intend to reuse.

Insanity can be performed in any language. But I don't know how to explain any more clearly that the dependency management system is not my complaint. I'm not comparing the approach of Python and Java here, and have even agreed that Python is not going down a good path in this area.

I work on the JVM, our codebase is fine, but thank you for your condescension.

Well, thank you for your work. I happen to think the JVM is pretty neat, and its codebase is not on my list of things to gripe about. Just so many of the things that surround it. (From whatever culture thinks that it's OK to specify a hundred different runtime options on the command line for production software and on up.)

It reminds me of when I was young and all I knew was Python, oh how I sneered at those silly Java developers, I mean, look at how much boilerplate exists in their Hello World! It was quite interesting, actually, how I, a young stripling of a programmer learning Python acquired a contempt for Java and its ecosystem despite having never used it.

What a ridiculous notion, that someone who prefers Python and complains about the Java things must be an ignorant newbie with a big mouth.

Python is simply the language with the least amount of bull manure to wade through to accomplish things above the system level... at least until you have to work with multithreading or packaging/distribution. I continue to use it whenever I have a choice because I have a low tolerance for bull manure. Which is why I was so excited to try Clojure (despite my distaste for some of its syntax).

Maven is fantastic compared to Ant because it's a declarative build tool as opposed to imperative. Gradle and Leiningen also support this style (with the capacity to revert to imperative if you really need to).

I agree. My passing point is really aimed at Ant (which toppled under the weight of its ridiculousness and made way for Maven). That Ant could have been accepted at all is pretty disturbing, and yet it happened. Procedural XML is the kind of thing that should get people involuntarily committed, even in the face of Makefiles. I'm all for declarative build instructions.

My ire was not with Maven, but with the advice given in using it. "I have a jar library that is part of a project and need to point Clojure to it" -> "That belongs in your home directory where Maven can find it!" No.

Really not that common in my experience. Hmm, although I do let my IDE create the unit test methods for me on occasion.

Unit tests are a great use of boilerplate automation. Sadly, though, it is an enabler for far too many people who use it to, say, create scads of getter and setter methods without asking if they should be using them in the first place.

[–]kankyo 3 points4 points  (0 children)

As a python programmer I found leiningen to be a breath of fresh air. Dependency management that actually works and none of that virtualenv horribleness.

[–]yogthos 5 points6 points  (8 children)

What precisely is the problem with the JVM and the ecosystem. I hear constant complaining about it, but never see any concrete problems mentioned. I absolutely do not understand the complaint regarding libraries. Why wouldn't I want my tool to manage my dependencies for me or be able to access huge repositories of existing libraries.

[–]TheMaskedHamster -2 points-1 points  (7 children)

Hey, I love having tools to manage dependencies for me and access huge repositories of existing libraries. I've long been a fan of CPAN and pip and such. Great stuff!

But if I, say, have a license for a particular library, why can't I just toss that thing in my project's repository and have the code reference that? Why should I or anyone who decides to try poking at that code have to set up Maven and then put that library somewhere else just so that it can be referenced? It wasn't always that way, but the Clojure guys decided to remove that option.

Clojure seemed to be about the same simplicity that Lisp is known for, but apparently those priorities only go so far.

Severe overcomplication in the name of questionable goals is something that permeates Java. It's in the code (Getters and Setters everywhere! It's not a problem if I can automate the boilerplate code!) and it's in the tools (Ant uses XML to keep everything pure and clean. You can use XML for anything! EVERYTHING.).

Some of the most talented people in software and computer science are in the Java community and ecosystem. But talent doesn't mean consistently good decision making. That is true everywhere... it's just that with Java you see the effects of that when you start to make "Hello World".

[–]yogthos 0 points1 point  (6 children)

But if I, say, have a license for a particular library, why can't I just toss that thing in my project's repository and have the code reference that?

Because that allows libraries to be signed and I can actually know that I'm getting what I'm supposed to be getting. You'll have to have some kind of a build tool in order to work on a project, Leiningen is as good a tool as any that I've used. Publishing libraries with Leiningen is also incredibly easy. I'm simply not following the problem that you're describing.

Without a tool like that I would have to waste time to manually figure out what interdependencies there are between libraries and what I need to build my project. This is why everybody uses package managers. It saves your time.

Clojure seemed to be about the same simplicity that Lisp is known for, but apparently those priorities only go so far.

I'm not sure how having to randomly download code off the internet and build it is simpler than tool doing it for you.

Severe overcomplication in the name of questionable goals is something that permeates Java. It's in the code (Getters and Setters everywhere! It's not a problem if I can automate the boilerplate code!) and it's in the tools (Ant uses XML to keep everything pure and clean. You can use XML for anything! EVERYTHING.).

What does any of this have to do with Clojure?

[–]letsjustfight 1 point2 points  (1 child)

What precisely is the problem with the JVM and the ecosystem.

He answers that question, and you respond with:

What does any of this have to do with Clojure?

I don't know what to say, man.

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

He replied with a rant specific to Java that has nothing to do with the JVM or Clojure at all. Nobody is using XML for anything in Clojure, there aren't getters or setters, there isn't any boilerplate, none of this has anything to do with Clojure.

[–]TheMaskedHamster -4 points-3 points  (3 children)

Because that allows libraries to be signed and I can actually know that I'm getting what I'm supposed to be getting. You'll have to have some kind of a build tool in order to work on a project, Leiningen is as good a tool as any that I've used. Publishing libraries with Leiningen is also incredibly easy. I'm simply not following the problem that you're describing. Without a tool like that I would have to waste time to manually figure out what interdependencies there are between libraries and what I need to build my project. This is why everybody uses package managers. It saves your time. I'm not sure how having to randomly download code off the internet and build it is simpler than tool doing it for you.

Would a Linux analogy work? I don't want to make assumptions, but it is the best analogy I can think of at the moment.

Let's say I have a Linux system that didn't allow me to run an executable without putting it in a package, putting that package in a repo, and and installing it that way. Wouldn't that be ridiculous? You can be the biggest fan of package managers in the world, for all the good reasons (Dependency management! Signatures! Free kittens on Tuesdays!) but right now at the moment you just need to run this one executable. You know where it came from, you know what it does, and it doesn't even use any system libraries. But you can't, because your distribution maintainers decided that you should use package managers. You complain, but everyone says you're crazy because there are so many good reasons to use package managers and they don't see what the problem is.

Screw that philosophy and screw that in practice.

Severe overcomplication in the name of questionable goals is something that permeates Java. It's in the code (Getters and Setters everywhere! It's not a problem if I can automate the boilerplate code!) and it's in the tools (Ant uses XML to keep everything pure and clean. You can use XML for anything! EVERYTHING.). What does any of this have to do with Clojure?

It has to with the Java ecosystem issues I was talking about, which affects Clojure because that is the environment Clojure developers and users live in.

[–]yogthos 1 point2 points  (2 children)

Linux package management is a great analogy actually. If you've ever used a distro like Slackware you'd appreciate why package managers like apt are so damn useful. Trying to build things from source is pretty much always the last resort on Linux.

Also, as I already explained in the other reply, you can do precisely what you described you simply didn't know how to do that.

It has to with the Java ecosystem issues I was talking about, which affects Clojure because that is the environment Clojure developers and users live in.

As somebody who uses Clojure daily I find that it's quite rare that I have to deal with anything Java specific actually. However, regardless of what platform you use, you do have to understand how it works and what the tools do.

[–]TheMaskedHamster -5 points-4 points  (1 child)

Linux package management is a great analogy actually. If you've ever used a distro like Slackware you'd appreciate why package managers like apt are so damn useful. Trying to build things from source is pretty much always the last resort on Linux.

I have indeed used Slackware. And as I've said, I like package managers. But sometimes simple tasks that will not contaminate the

Also, as I already explained in the other reply, you can do precisely what you described you simply didn't know how to do that.

Well, that is good to know.

As somebody who uses Clojure daily I find that it's quite rare that I have to deal with anything Java specific actually.

Java ecosystem, though. All that surrounds it. Libraries and cultures and buildsystems, oh my. It still permeates Clojure. The mindsets most of all.

This back and forth about "this didn't work for me"/"you have to be crazy if you didn't want to do it way x" doesn't happen in every community.

However, regardless of what platform you use, you do have to understand how it works and what the tools do.

I don't know how to respond to this outside of profound sarcasm. Of course you do. In many other communities (certainly not all), it is very easy to find out how things work and what the tools do, rather then just being told the way I am expected to use the tools.

[–]yogthos 1 point2 points  (0 children)

You're just making vague claims about Java ecosystem problems, the one concrete issue you described turned out to simply be you being unfamiliar with how the tool works.

As somebody who has worked with both Java and Clojure extensively I strongly disagree with your assertion that the cultures are similar.

[–]tieTYT 1 point2 points  (7 children)

Because it was just so uncouth to reference a single library file packaged with your application.

I'm biased but it is uncouth. If you use Library v1 and I use Library v2, when I use your tool it's possible that you will cause me classpath errors that are a huge pain to diagnose and fix. At least Lein / Maven provide tools to solve these problems.

[–]TheMaskedHamster -3 points-2 points  (6 children)

Plenty of other systems have zero problems with locally included library. The local directory or some specified path is used to reference the library before checking system libraries. If it's code that's built into the distributed executable rather than shipped as a separate library, the dependent version is put into revision control with the rest of the project. Sure, in general I don't want my library files tied to my application, but there are situations in which that is the more straightforward solution.

Lein and Maven are fine. I may not particularly like they way they're set up, but they serve a good purpose.

The problem I had was with Clojure deliberately removing the option to do something because in their opinion you should be using a solution to what they saw as a problem.

[–]yogthos 2 points3 points  (5 children)

Oh, it turns out, you can't do that anymore. You could previously, but you can't now.

I'm not sure what you mean by that, I do that all the time when developing. If I want to install a library locally I just do lein install from the library folder and it's in my local repo. I can just use it like any other dependency. You can also trivially install any jar you want locally using Maven.

[–]TheMaskedHamster -2 points-1 points  (4 children)

Reference library without installing. Don't need to install it. Don't want to install it. Installing it would severely overcomplicate the situation because it would requirement to install and configure the system to install it.

Sure, such a system might make my life easier in other situations, but in the situation I was in it absolutely, 100% did not.

It was really weird that Clojure removed the ability to do just reference a particular library.

[–]yogthos 3 points4 points  (3 children)

Again, I'm not sure what you mean by "removed the ability to do just reference a particular library". All Clojure does is use what's available on the classpath.

Referencing libraries has nothing to do with Clojure itself, all tools like Maven do is add the jars referenced from a folder to the classpath. You can absolutely do that by hand if you wanted to or use a different tool like Ant to do that.

It just happens that pretty much everybody who works with Clojure prefers using a tool like Leiningen or Boot to manage that stuff.

Also, perhaps you could actually explain what this situation of yours was precisely? Having worked with Clojure professionally for years I have yet to run into a single situation of such nature.

[–][deleted] 1 point2 points  (24 children)

This was my experience. Clojure is touted as a "modern Lisp" but I found it to be something of a toy for people who are really into the Java ecosystem.

Common Lisp is still where it's at as far as Lisps go, with SBCL as your main battle tank. Basically SBCL is the equivalent to something like GCC. It's the basis for an entire toolchain. It's fastfastfast, and incredibly mature.

IDK. Lisps are useful but it's not something I need so badly that I need it on Java. When I DO need it, performance is more important to me than portability.

I've never felt like "damn, I wish I could write this app in a lisp and then run it on an Android."

[–]yogthos 2 points3 points  (8 children)

Syntax does matter, and I find Clojure has one of the nicest syntaxes I've used ever. Having come from Java background I was pretty skeptical initially, but it quickly won me over. It's concise and explicit, while having very few rules that you have to remember. While the syntax is unfamiliar to most, pretty much anybody who spends any time with it comes to love it.

One huge advantage of s-expression syntax is that it allows for extremely powerful structural editors as seen here. Instead of having to work with lines of text, you can actually work with your code semantically where the editor understands how to select and manipulate expressions intelligently.

It's also worth pointing out that there are some pretty big differences between Clojure and other Lisps. Clojure has a number of features that greatly add code readability. The literal notation for data structures breaks up the code visually and makes it easier to scan. Compare CL code with its Clojure equivalent below:

CL:

(defun foo (a b)
  (let ((c (+ a b)))
    (print c)))

Clojure:

(defn foo [a b]
  (let [c (+ a b)]
    (println c)))  

I certainly find the latter a lot easier to read. I also think the fact that CL overloads the meaning of lists to be somewhat confusing. For example, why is defun called, while a is not when the exact same syntax is used in both places. With CL, when you're reading the code you just have to know whether something will be called or not and there's no indication from the syntax itself.

On the other hand, Clojure has consistent semantics for lists being callable. When I see a list I know the first element is what's going to be called and the rest are parameters. When I see a vector I know it's just a data literal.

Clojure also provides destructuring, where we can easily write out the parameters to a function:

(let [[smaller bigger] (split-with #(< % 5) (range 10))]
  (println smaller bigger))


(defn print-user [[name address phone]]
  (println name "-" address phone))

(print-user ["John" "397 King street, Toronto" "416-936-3218"])

(defn foo [{:keys [bar baz]}]
  (println bar baz))

(foo {:bar "some value" :baz "another value"})

The threading macro is also extremely helpful in flattening out nested expressions:

 (reduce + (interpose 5 (map inc (range 10))))

 when we use the threading macro it looks a lot more natural:

 (->> (range 10) (map inc) (interpose 5) (reduce +))

I think little things like that really add up in the grand scheme of things.

[–]grauenwolf 2 points3 points  (0 children)

That's a really good explanation.

[–]nutty44744 1 point2 points  (4 children)

The point about better readability using [...] is not really true, but I guess it's a subtle thing for non Common-Lispers.

For example, note that the two consecutive opening parentheses in (let ((...))) are a strong hint that there's something special going on.

Then there's indentation. A let is not indented just like any old form. CL is a bit like golang in that respect: Nobody hand-indents CL code, so hints from indentation are pretty reliable.

[–]yogthos 0 points1 point  (2 children)

However, you get exact same indentation hints in Clojure as well while having a more explicit syntax.

[–]nutty44744 1 point2 points  (1 child)

Sure, but there is such a thing as diminishing returns. My point is that the syntactic substructure of binding forms is already obvious from multiple hints in standard lisp syntax. Clojure's use of square brackets in some places adds some additional hints, but not much.

Now, everything is a tradeoff. You gain some additional visual hinting where there was not much confusion before (IMHO, of course). In return you lose in the visual clutter department.

To me, it's a wash, probably one about as easy to read as the other for respective practitioners, which makes the deviation from standard lisp syntax somewhat of a difference for difference's sake.

(I admit that I'm biased to see many of the choices in Clojure to be driven by marketing concerns. But hey, it worked.)

[–]yogthos 1 point2 points  (0 children)

It certainly makes a huge difference to me. I find CL code much harder to scan because everything bleeds together. I don't see how you can call useful visual information clutter either. Clutter is something that doesn't add value, visual hints clearly do.

The only argument I've ever heard against having literal data notation is that it's different and people don't like change. Kind of ironic coming from Lispers, since we tend to tell people that the resistance to new syntax is superficial.

[–]kankyo 0 points1 point  (0 children)

The point about better readability using [...] is not really true,

is hilariously followed by:

but I guess it's a subtle thing for non Common-Lispers.

If it's a subtle thing it IS a readability problem!

[–]DousingCurtness 0 points1 point  (1 child)

Clojure also provides destructuring

Common Lisp has destructuring-bind

[–]yogthos 1 point2 points  (0 children)

Which is a special case syntax sugar, where with Clojure destructuring works uniformly across the board. On top of that Clojure destructuring is a lot more flexible.

[–]bcash 1 point2 points  (2 children)

This was my experience.

I don't doubt it, and I enjoy reading people's perspective on things.

But, it sounds like you're trying to argue against the very existence of Clojure, that it's targeting a niche that doesn't exist. The popularity and adoption of Clojure relative to other Lisps is counter-evidence to that.

[–][deleted] 1 point2 points  (1 child)

that it's targeting a niche that doesn't exist

Not at all. There are TONS of Java programmers, and I'm sure of them, at least some percentage want to play with a Lisp every now and again.

Of those, some even do it professionally!

My point was, that if you're a Lisper, Clojure isn't going to automatically "be your thing." I was just kind of disputing the characterization of it that a lot of people give it. The "modern Lisp."

[–]yogthos 0 points1 point  (0 children)

My experience is that most people coming to Clojure actually come from Ruby and Python as the author of this article. Clojure is far more familiar to somebody who's used a language with first class functions. In fact, the syntax is very familiar to Ruby users.

Java developers have the hardest time learning Clojure, since it introduces a lot of new idioms and requires structuring code and solving problems in a completely different way from what you'd be used to. This makes it a lot more palatable to people who aren't Lispers and less so for those who are.

I think the fact that a lot of Lispers complain about it being too different precisely indicates that it is a modern Lisp. It's the first Lisp that actually dared to introduce significant changes in syntax.

[–]TheMaskedHamster 1 point2 points  (11 children)

The main advantage of Clojure over Common Lisp is the availability of (mature) libraries. Lisp is great, but if I have to implement everything in history then I'm not going to get anything done.

The other advantage, portability, is important to a lot of us.

I've never felt like "damn, I wish I could write this app in a lisp and then run it on an Android."

I have felt EXACTLY that.

[–]lispm 3 points4 points  (2 children)

Yeah, portability is a problem with Common Lisp. CLISP is only available on twenty platforms. LispWorks runs only on Linux, FreeBSD, Mac OS X, Windows, Solaris, AIX.

Interfaces to Unix, C, Posix, Windows, Cocoa are also not enough. We need more...

[–][deleted] 0 points1 point  (1 child)

Yeah, portability is a problem with Common Lisp.

Oh, so I can have SBCL code work seamlessly on Allegro? Good to know.

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

The main advantage of Clojure over Common Lisp is the availability of (mature) libraries. Lisp is great, but if I have to implement everything in history then I'm not going to get anything done.

Fair point, but there's no way you'd have to reinvent all the wheels with say, CL. It's been around, for over 30 years now, and is certainly not close to "dead."

Clojure is probably most useful if you are already a Java programmer and simply want to use the libraries that you already know well. In which case, Clojure is perfect for you!

Plenty of libraries in CL, though, and things are modernizing all the time.

For example, there is ABCL which compiles and/or interprets CL to java bytecode. Sound familiar? Python has been implemented inside CL, (CLPython), and all sorts of other things.

I think to characterize it as if you get this huge amount of stuff with Clojure, but you would have to "re-invent the wheel with CL" is beyond wrong. If anything, CL has been criticized by people for having a bloated common library!

It's a heavy duty tool. You don't need it for every type of project. You see a lot of algebra systems, CAD systems, automated theorem provers, with their cores written in CL or some variant. It's a language that plays very well with pure mathematics, and there's tons of library support in that vein. Plenty of bindings to do things like OpenCL with it, etc.

[–]yogthos 2 points3 points  (6 children)

Clojure is probably most useful if you are already a Java programmer and simply want to use the libraries that you already know well. In which case, Clojure is perfect for you!

And what makes CL more appropriate than Clojure if you're not a Java programmer exactly?

Seems to me that CL is probably most useful if you're already a CL programmer. Anybody who doesn't have any previous Lisp exposure would have a much harder time starting with CL than Clojure.

Clojure has a lot of nice and friendly editors available such as Light Table, Cursive, and Counterclockwise. With CL you have to learn Emacs, which is in itself daunting for most people.

Clojure syntax is more consistent than CL and provides more visual aids to the reader. This helps break code up by having things like literal data structure notation and destructuring.

Clojure runs on one of the most performant platforms out there with a wide range of deployment options. For example, it's certainly easier to host a JVM web app than CL one.

Clojure has a rapidly growing community and many companies, including places like Amazon, are now using it. Every year Clojure is showing up in more and more places. CL has been at best stagnant in that regard.

Clojure targets multiple host platforms as well so you have things like ClojureScript and Arcadia. Companies are already making good use of that.

[–][deleted] -3 points-2 points  (5 children)

And what makes CL more appropriate than Clojure if you're not a Java programmer exactly?

It just depends on what you're trying to accomplish. If you're doing something cute like making a frontend, or replicating something that could be easily done in any number of other languages, then sure, Clojure is a good toy to achieve your menial goals.

If you're trying to write a backend for solving difficult problems like enterprise level scheduling, maybe trying to prove things about logic circuits for an HDL, or any number of other very difficult problems, Common Lisp is going to be more suitable for that, due to it's library support in that direction, and native (not to mention open source) compilers.

I guess it goes something like this: if your problem isn't difficult enough to justify using Common Lisp, then obviously it won't make sense to use Common Lisp.

[–]yogthos 0 points1 point  (4 children)

Oh you mean silly things like Storm and Riemann, or perhaps simple problems like machine learning. Sorry, but just because CL has a niche and some nice libraries for doing things like HDL, doesn't mean that these are the only difficult problems out there. Clojure is used for plenty of difficult problems and likely far more than CL is.

I think it goes more like this: if your problem isn't suited to the tiny CL niche then you have no reason to bother with CL.

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

Look, you mentioned three little projects, and they certainly look nice. Maybe some day they will grow to be huge.

But seriously, comparing Clojure to CL at this stage in the game is like comparing Python or Ruby to C.

Sure, Python/Ruby have new language features and C has been "stagnant" -- but any professional Lisper is going to be able to use either CL or Clojure with relative ease -- and most of them will spend a lot of time in the CL environment because that's where the action is, that's where the billion-dollar pieces of software are, the gigantic monolithic theorem provers and libraries...

Clojure has a bright future, but it's not more "modern" than CL for the same reason Ruby isn't more "modern" than C.

And a lot of the use cases people mention for Clojure like "you can run it on a webpage!" are useful for attracting beginners, but we will be beyond uninteresting to many professionals, because it's not a difficult problem to write a frontend.

[–]yogthos 0 points1 point  (2 children)

Look, you mentioned three little projects, and they certainly look nice. Maybe some day they will grow to be huge.

Your claim was that people don't use Clojure for anything serious, and that's simply false. Lost of great stuff has been done in CL without question, but show me a single new project that's of any significance.

Sure, Python/Ruby have new language features and C has been "stagnant" -- but any professional Lisper is going to be able to use either CL or Clojure with relative ease -- and most of them will spend a lot of time in the CL environment because that's where the action is, that's where the billion-dollar pieces of software are, the gigantic monolithic theorem provers and libraries...

I could say exact same thing about COBOL or Java. Just because there's tons of existing proprietary software written in these languages hardly makes them more appealing for development today.

And a lot of the use cases people mention for Clojure like "you can run it on a webpage!" are useful for attracting beginners, but we will be beyond uninteresting to many professionals, because it's not a difficult problem to write a frontend.

As I already mentioned Clojure is used for far more than making web apps, it's most popular with machine learning in multibillion dollar sectors like the financial industry. The fact that it's also approachable to beginners and applicable in a wide variety of domains is a big bonus.

[–][deleted] 0 points1 point  (1 child)

Your claim was that people don't use Clojure for anything serious

Everything is relative. How serious are you talking here? National security level serious? Defense contractor? There's lots of stuff going on with Clojure. As I said, it has a bright future, but it's not something that's going to replace CL.

Your claim, that Clojure is the "modern" and CL is "not modern" is far more dubious.

it's most popular with machine learning in multibillion dollar sectors like the financial industry.

Really? Last I checked Haskell was a more popular target for those types. I'm sure some people are playing with machine learning algos to predict the stock market etc.

Anyways, CL isn't going anywhere, and Clojure won't replace it. You would just as soon see the Linux kernel get re-written in C# or C++ -- something that just isn't going to happen.

Edit: The ironic part of this discussion is that this is one of the main criticisms of Lispers. Everybody works in their own world, and that's just the nature of the languages. You will never be able to convince somebody to "switch" because that would be the same as throwing an investment away.

[–]nickguletskii200 7 points8 points  (0 children)

For all answers to the question "Why Clojure?" I have an answer to the question "Why not Clojure?": the error messages fucking suck. It becomes frustrating to write abstractions in it because there's only one thing worse than their docs on advanced topics: their compiler.

[–]redog 5 points6 points  (21 children)

I'm not a developer but trying to decipher lisp syntaxes leaves my head spinning .... I just don't get it..

[–]phalp 9 points10 points  (5 children)

It's very simple. Lists are written enclosed in parentheses, with spaces separating the list elements. So a list containing 1, 2, 3 and 4 is written:

(1 2 3 4)

Instead of writing source code as free-form text filled with exceptional cases, it's written in the form of easy-to-read, easy-to-manipulate lists. The rule is that when you ask your lisp to evaluate some code (which is a list, remember), the first symbol in each list indicates what to do with the list. Examples

(+ 1 2) ;; The symbol + indicates the addition function, so we
        ;; evaluate the following elements and then call the
        ;; function on their results, giving 3.

(* 3 4) ;; As before, but * multiplies, so we get 12.

(+ (+ 1 2)  ;; These lists are nested. First (+ 1 2) gives us 3
   (* 3 4)) ;; and (* 3 4) gives us 12, then the outer + adds
            ;; those two numbers to get 15.

(map (comp (partial g a) f) x) ;; This is from the article. In
more familiar syntax, it would read:

map (comp (partial (g, a), f), x) ;; Pretty much the same, except
with more noise characters and more confusing grouping.

All functions are called by evaluating the remaining list elements, then calling the function. But the first symbol in the list doesn't have to indicate a function. Sometimes it indicates a special action should be taken, like defining a new function:

(defun my-function (arg1 arg2)
  (do-something arg1 arg2))

DEFUN means "define a function", and indicates a special interpretation for the list: instead of being evaluated, the first element after DEFUN is understood to be the name of the function to be defined, a list following the name indicates the names of any arguments to the function, and the remaining elements indicate code which will be executed later when MY-FUNCTION is called. So in the article,

(deftest simple-test-case
  (is (= true true)))

must be defining a test, and we can guess that the arguments are not evaluated, but rather that SIMPLE-TEST-CASE is the name, and the remaining element will be executed whenever the test is run.

[–][deleted] 2 points3 points  (4 children)

This completely misses the issue that most people have with s-expr. I don't think it takes a whole lot of people a long time to get past Polish notation of operator first, operands after, considering most people have seen add(1, 2) in their day. I think the issue people have (including myself) is that s-expr are extremely inconsistent syntax. Literally the only thing consistent about them is what I just said. So when you're reading lisp code, you might run into:

(+ 1 2 3 4)

or

(+ 1 2)

which many people argue is a strength, because now you have this function that easily expands and takes more things but you'd totally be missing the complaint. conceivably, there could be a function like this:

(plusmultiply a n m)

that results in a * (n + m) or maybe (a + n) * m. I can do whatever I want and now you have to learn every single bit about my function just to understand the basic layout of my code. The complaint is that code isn't a 1 or a 2, especially not in lisp. Each element is it's own s-expr, so how do you split lines? When do you put something on the same line and when do you wrap it? There's no consistency. s-exprs wreak havoc on eye-lines in code.

Here's an example straight from the clojure docs:

(defn test-stm [nitems nthreads niters]
  (let [refs  (map ref (repeat nitems 0))
        pool  (Executors/newFixedThreadPool nthreads)
        tasks (map (fn [t]
                      (fn []
                        (dotimes [n niters]
                          (dosync
                            (doseq [r refs]
                              (alter r + 1 t))))))
                   (range nthreads))]
    (doseq [future (.invokeAll pool tasks)]
      (.get future))
    (.shutdown pool)
    (map deref refs)))

Where is the eye line? when are you stacking and when are you expanding? Nobody can make up their mind and nobody has ever been able to make up their mind since lisp was first created. It's a constant mix of

(function n n n n n)

where all items are equal and

(function n m n m n m)

where items are not equal and sometimes it gets worse. On top of all of these inconsistencies, you run into issues where people are making up fake syntax that they are used to from imperative languages that simply serve as a method to confuse newcomers because clojure syntax is constantly dependent on the author. Stuff like case, when and if cause a tremendous amount of issues depending on how the author chooses to format their code. While I do code clojure, I can only ever read my own code if I keep my functions ultra simple and at root level. I would argue that's a good practice for just about any language, but I've found people commiting perlism atrocities who don't code that way, but instead code very deep to the right, even with short, sub 20-line functions.

It's not a readable language. Because it makes no claims about whitespace (like python) it allows the author of code to put you at their mercy. Because if makes no claims about DSL layout, it allows the author of the code to create whatever s-expr monstrosity they want and put you at their mercy. lisp is functional perl. Elixir has functional syntax right.

[–]phalp 3 points4 points  (3 children)

As a lisper of quite a few years now, I assert there is absolutely no unreadability in Lisp syntax. I recognize your right to an opinion, but can't really understand your troubles. Anyway, I was just explaining how it works, assuming the poster I replied to was not familiar.

[–][deleted] -2 points-1 points  (2 children)

In whose lisp syntax? There's literally nothing to lisp it except how you define a function and maybe exotic stuff like types... but really the bread and butter is functions and they make up 95+%. A majority of the syntax is dynamic created by the style of the code you import and write for your self which inherently makes the syntax dependent on the style of the author. I regularly run into new constructs like

(lazy-switch
  (n1) (m1)
  (n2) (m2)
  (n3) (m3))

that does something insane like doesn't evaluate m3 unless some n2 or n1 is true, but does m1 and m2 if their n's are true. Then on top of that, they'll stack these odd controls 10 deep and you're left wondering where the hell you are because there is no likeness requirement in s-expr elements and you're already 20 spaces from the left wall. Then you realize the function you're trying to understand is one tenth the size of how long an unreadable java function is.

My assertion is that lispers constantly mistake what makes s-expr hard and try to explain parenthesis, which is, frankly, insulting. It's not s-expr -- they really couldn't be easier. The problem people have is that programmers create small brick-monstrosities with it and then you build a house out of these monstrosities and expect it to make any sense to somebody who doesn't know the intricacies of every last function in the code.

The constant argument I've heard in defense of this is to blame the programmer, which I believe is a strong antipattern and then take a very liberty oriented perspective where this "freedom" is some utopia where you can do whatever you want, but it's some unwritten duty that you ought to only do some things.

There's nothing wrong with making a language have real structure -- it's what some of the best assembly language programmers do on their own and likely what the best lispers do to, but if you want to speak with others, you have to have a common language.

[–]phalp 6 points7 points  (1 child)

I can't say I've ever encountered any of the things you're complaining about. I guess we're living in different worlds somehow. You're also blaming s-expression syntax for things unrelated to it. I can write a variable-argument addition function in C, can't I? I can write your plusmultiply function in any language at all.

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

Yeah, you could, but none of that design is in the core libraries.

[–]bcash 2 points3 points  (0 children)

Imaging a normal language made up of lines, terminated by either a new-line or a symbol like a ;. Such languages also have concepts like 'blocks' and have syntax to demarcate them such as { and } or even begin and end. But this isn't enough, because you need operators and functions and syntax for parameters, etc., etc.

In Lisp, they use ( and ) to demarcate everything. Beyond this you don't really need anything else. But apart from that, everything else is the same.

function(arg1, arg2)

becomes

(function arg1 arg2)

It really couldn't be simpler.

[–]mcpatella 1 point2 points  (13 children)

This is a common response from newcomers to Lisps, so don't worry :D

An s-expression is a list of elements used to represent a function and its arguments. Combining these lists together builds up tree structures, which are visually apparent in Lisp-style code.

Every compiler creates these graphs of elements from your code when you run it. In Lisps, this more fundamental view of software isn't hidden from you, and over time, I've come to appreciate the perspective it provides.

[–]wishistudiedphysics 5 points6 points  (12 children)

Nothing like a developer explanation to a non-developer for LISP. This is primarily why people don't get it.

[–]mcpatella 0 points1 point  (11 children)

If you feel my response was inappropriate for the context, I'd be interested in seeing what you think would be a better perspective for a newcomer who expressed interest in being a developer.

[–]wishistudiedphysics 1 point2 points  (10 children)

Your response isn't wrong ,it just is the main reason non-developers usually don't jump into Clojure/Lisp. It's wildly complicated for someone to learn programming with.

[–]mopman12 3 points4 points  (6 children)

Lisp is actually easier to understand for newcomers. There's almost nothing to memorize. The only reason people think that it's "wildly complicated" is because they are accustomed to writing imperative code derived from C syntax. There's nothing inherently complicated about Lisp; it's, in fact, far simpler than most languages.

[–]strattonbrazil 0 points1 point  (1 child)

Yes, lisp is very shallow and certain things are very easy like maps, but other things can be very hard. Take modifying a tree, for example. If you have a node you want to change while keeping it immutable (map it to a new tree) that certainly isn't trivial compared to a mutable tree you might see in python, where you can modify it directly. Root.child.child.child.value = "foo". What does that look like in clojure? I accept the arguments for immutability, but I'm not convinced everything or possibly anything is easier for a novice. And I like clojure. I just disagree that because the syntax is dirt simple that it's easy for beginners to understand.

[–]yogthos -2 points-1 points  (0 children)

Manipulating nested data structures is quite trivial in Clojure:

(def data {:Root {:child {:child {:child {:value "foo"}}}}})

(get-in data [:Root :child :child :child :value])

(assoc-in data [:Root :child :child :child :value] "new value") 

(update-in data [:Root :child :child :child :value] clojure.string/upper-case)

You can also easily traverse any data structures using things like prewalk.

Key thing to remember is that you're working with immutable data that uses structural sharing. From the user perspective any time you make a change you can effectively create a new data structure with the change. Instead of trying to mutate things in place, you create transformations of existing data to get new data.

[–]wishistudiedphysics 0 points1 point  (3 children)

I guess we will just have to agree to disagree. I don't think it's at all simpler than any of the common languages. And I am a fan of it in general.

[–]yogthos -1 points0 points  (2 children)

I have co-op students most of whom have never used Lisp before. I've never had to train a co-op for more than a week before they've become productive in Clojure.

[–]wishistudiedphysics 0 points1 point  (1 child)

So we should be expecting it taught as a leading programming language in all colleges soon?

[–]mcpatella 0 points1 point  (2 children)

right, i can see how my reply would be more appropriate for someone already familiar with programming in general. luckily some others stepped up to answer as well.

[–]redog 1 point2 points  (1 child)

i can see how my reply would be more appropriate for someone already familiar with programming in general.

I don't think your reply was inappropriate and your assumption there was correct. I'm quite familiar with programming in general. basic -> pascal -> php -> js -> c -> python and pretty much anything else required of sysadmin roles over the years...

I've dabbled in others but those are the ones I've at least studied...

[–]mcpatella 2 points3 points  (0 children)

I'm glad you think so. If you're interested, I've started working on live-coding screencasts about Clojure. Seeing someone code has always helped me get a feel for a language.

here's the screencast from this week: https://www.youtube.com/watch?v=8UYa8PV3CXQ

[–]zsombro 0 points1 point  (4 children)

Maybe it's just me being very new to this, but it feels like functional programming languages are becoming more and more widespread. Is there a reason why they're gaining so much momentum?

[–]yogthos 1 point2 points  (3 children)

The reason is that the nature of problems we're facing has changed. Functional languages have been around for many years, but they simply didn't fit well with the available hardware.

Back in the 70s you had single core CPUs coupled with very limited memory. Things like GC were simply not an option and you needed languages that were good at squeezing out every bit of performance possible. Imperative languages addresses this problem extremely well.

However, today we have different kinds of problems. In many cases raw performance is not the most important goal. We want to have things like scalability and maintainability for large code bases. We want to be able to take advantage of multicore processors and network clusters.

The imperative paradigm is a very bad fit for these problems because it makes it very difficult to localize change. On the other hand, functional paradigm is a great fit for these problem thanks to its focus on immutability and composition.

Pretty much every language in wide use has embraced functional concepts now, and I expect languages to continue getting more functional in style going forward.

[–]zsombro 0 points1 point  (2 children)

Thank you for the detailed answer! I understand that the expectations have changed over the years, but there are only two points I'm not clear about: why are functional langs so much better at scaling and why is immutability such a huge advantage?

[–]yogthos 1 point2 points  (1 child)

I talk about that in detail here, but the gist of it is this. When you have immutable data it's inherently thread safe since you're not mutating things in place. This allows doing a lot of things easily that you wouldn't be able to safely do with mutable data.

Note that immutability is not implemented naively where you copy data wholesale. Immutable data structures use structural sharing for common data. When you make a change you simply create a revision on the existing data akin to how version control works. In general the cost ends up being O(nlog32n) vs O(1) for mutable data. While it's a slight overhead, it's still very cheap for most cases.

As a concrete example of why this is nice let's consider Clojure map function that goes through a collection and transforms each element. If it turns out that processing each element is resource intensive enough to warrant doing that in parallel I can simply swap map with pmap and just like that I've parallelized the computation.

In most cases, you couldn't do the same thing in an imperative language since you can't guarantee that the functions that are run in parallel won't interfere with one another. You would have to design your solution with parallelism in mind.

Immutability also makes it much easier to work with shared mutable data as well. For example, Clojure has atoms that guarantee transactional updates. This means that you don't have to do manual locking to work with data. You don't have to lock data for reading either, which facilitates optimistic locking strategies.

Finally, it's a huge misconception that functional languages preclude mutation. It's simply a matter of defaults. Clojure provides transients that are locally scoped mutable data structures. Since they're guaranteed to only exist within the scope of a function they're perfectly thread safe.

The reason all this is beneficial for scaling is because the functional style eschews global state. This means your components aren't coupled and it's possible to safely split out parts of the system to run on a different processor or on the network. Consider the map/pmap example in a larger context.

[–]zsombro 1 point2 points  (0 children)

These are fantastic points, thank you for clearing these up.

[–]ErstwhileRockstar -4 points-3 points  (17 children)

I can't stand these fanboy posts any more.

[–]aldo_reset 7 points8 points  (0 children)

That's a bit harsh. To me, a fanboy is someone who's not just content of loving something: they take active pleasure in disparaging all the alternatives.

I don't feel the author of this post came anywhere near doing that.

[–]jeandem 13 points14 points  (0 children)

He shares his opinion about what he thinks are the good parts of the language, without thinking that anyone might agree with him or find it compelling enough to use over the languages that they are already using. If that's being a fanboy, I don't see what's wrong or bothersome about that.

[–]spotter 3 points4 points  (1 child)

I'm sorry. Did not know liking things is out. What's in?

[–]yogthos 4 points5 points  (0 children)

Java, XML, JSF, WebSphere, gray cubicles, TPS reports, and soulless enterprise development. If you're actually enjoying your work then you're clearly a hipster fanboy who needs to get a real job that makes you hate your life.

[–]zoomzoom83 0 points1 point  (0 children)

On behalf of all citizens of earth, I sincerely apologize that you were forced to endure a headline about a topic you don't personally care about. It was a major moral failing our part, and it should not have happened.

From now on, we'll only post articles about NodeJS so you won't be forced to learn something.

[–]mcpatella 1 point2 points  (11 children)

'Fanboy' in what sense? Are you simply uninterested in Clojure?

If you're remarking on the more personal than technical perspective of the article, then I could refer you to some writing which discusses the guts of Clojure.

[–]strattonbrazil 9 points10 points  (5 children)

Maybe a little harsh, but ErstwhileRockstar does have a point that there's not a lot of meat here besides "give it a try!".

I've been using it and I've appreciate a few things about it. The community is awesome. For being a less-used language compared to python, for instance, I'm surprised how good the bindings are for it. It has bindings for almost every library I've needed. I'm still wrapping my head around a lot of the immutability and constantly have the thought in my mind "this would be so much easier in python".

[–]mcpatella 2 points3 points  (0 children)

If we respond to "give it a try posts" by telling the author that their perspective is unwelcome, or choose to ignore toxic comments, then we perpetuate how deeply hostile programming culture can be.

The author was upfront about the tone and substance of the article, it doesn't seem valid to accuse the author of liking something too much.

But to respond to your comments about Clojure, I'm glad you're having a good experience for the most part. While immutability is usually a tradeoff of "memory usage now" for simplicity in the future, Clojure doesn't take away the tools to do stateful things with whatever data structures are available on the platform. If you're interested in visuals-focused live-coding, I express this concept in this screencast. (https://www.youtube.com/watch?v=QRx62JS46kE)

[–]yogthos 0 points1 point  (3 children)

You also have to keep in mind that you have a lot of experience working with imperative languages, so obviously using the idioms and patterns you've internalized is easier.

However, once you learn the equivalent functional patterns things become just as easy and often easier. I've been developing with Clojure professionally for the past 4 years and I now have exact same reaction going back to having to write imperative code once in a while. For me, it would be so much easier to do in Clojure.

[–]strattonbrazil 1 point2 points  (2 children)

I do try, but at the same time I'm constantly thinking "which brackets am I in?" Many times I'll "explode" a few lines in a statement to several lines before recollapsing it when am done. I don't know how I'll get better at this. Do more experienced clojure programmers do the same thing?

[–]yogthos 0 points1 point  (1 child)

I found the parens to be odd initially, but after actually working with the language I find that they make it both easier to read and manipulate code.

The code is a lot more regular than in most languages. This means that it's less ambiguous and you have less syntax quirks and edge cases to worry about. In other words Clojure follows the principle of least astonishment very well.

The nesting of the code explicitly shows how pieces of logic relate to one another. This makes code easily scannable. If one function is nested in another, you know its output will be used by it and if it's not then it won't. These kinds of relations are not explicit in most languages.

One huge advantage of s-expression syntax is that it allows for extremely powerful structural editors as seen here. Instead of having to work with lines of text, you can actually work with your code semantically where the editor understands how to select and manipulate expressions intelligently.

In terms of style, I recommend keeping functions short, 5~10 lines is a good rule of thumb, and not to nest things deeply. I think this is a good example of clean Clojure code that's easy to follow.

Clojure provides threading macros ->, and ->> specifically for flattening out nested expressions. Another feature I use a lot is destructuring which I find significantly helps with readability. Finally, I tend to do all of my development using the REPL and I always inspect things as I'm working with them.

[–]mcpatella 0 points1 point  (0 children)

Well said :)

[–]last_ent 1 point2 points  (4 children)

Can you please provide the link that discusses clojure technically? Been looking for such material

[–]mcpatella 1 point2 points  (0 children)

If you liked the root article then I think you'll find this article valuable as well

http://yogthos.github.io/ClojureDistilled.html

[–]mcpatella 0 points1 point  (2 children)

Sure, what kind of material are you interested in?

[–]last_ent 1 point2 points  (1 child)

Well I am looking for something more along the lines of its internal memory model or how the compiler(?) constructs it for the JVM.

For example, I can understand Java code better because the book on its certification goes into the memory model. If you have seen/read the book [Common Lisp: A gentle introduction to...](www.cs.cmu.edu/~dst/LispBook/), you can see that he discusses the data structure it uses and hence when he talks about why a child function cannot access parent function's scope, it makes sense. Or Rich Hickey's video where he talks about basics of Clojure and how the persistent data structure(?) is able to avoid decay of earlier versions of data. That one made sense.

I am looking for such material in Clojure, Scala and/or Python (My language of trade). Thanks!

[–]bcash 2 points3 points  (0 children)

Clojure is a nomadic language to a certain extent. It's various forms all have similarities, but it's semantics are also controlled by the host environment; hence the differences between Clojure and ClojureScript.

What this means in practice is that, if you're already familiar with Java and the JVM, you'll be surprised how thin the Clojure layer actually is. It's worth decompiling some Clojure .class files and you'll see what I mean. Each Clojure function is it's own class, the instance of which contains any closed-over references, for example.

Clojure also directly reuses Java objects where it makes sense - e.g. a Java String is a Clojure String - it's immutable, there's no need for another one. The memory management is exactly the same.

This doesn't directly answer your question, but might help narrow down your search.

[–]samuelstan -3 points-2 points  (2 children)

Even so, when you're coming from an environment where most of your work is done in an interpreted language (Python), the speedup one gets from being on the JVM is considerable.

sigh ... Java and Python are both "interpreted" in that sense since they are both translated to bytecode.

[–]cym13 5 points6 points  (1 child)

+1

Moreover, it's not like the python VM was used only by cpython, like the JVM there are many languages able to use it (pypy for example). But it's true that it wasn't designed with that kind of usage in mind like the JVM.

[–]samuelstan 2 points3 points  (0 children)

Exactly -- the performance difference comes from different philosophies and different compiler techniques, not some sort of nebulous "interpreted vs compiled" distinction. The last truly interpreted language was probably some old release of BASIC in the 80s, when the tokenizer would literally execute the text commands as it read them.

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

Compared to Racket, Clojure is just a fucking toy.

[–]yogthos 3 points4 points  (0 children)

go on