Why Clojure over Python?

If that’s your expectation, you have made a mistake somewhere.

Functional programming is very different from OOP. If one is not willing to undergo a period of “I don’t know how to program any more”, it would be hard to overcome the obstacle that was created by the years of OOP experience.

I had gone through this myself about 7 years ago. I had been programming for 20 years by then, but with Clojure, I suddenly didn’t know how to program any more. Couldn’t do the simplest thing.

I did not try to justify my inability by going to a Clojure forum to talk down the language, instead I headed to 4clojure.com, and cut my teeth on those little exercises. Then one day, it clicked. The rest is history.

3 Likes

Fun fact: The Scheme language (a functional-ish lisp) was originally intended by its authors Guy L. Steele and Gerald Sussman as a tool for exploring object-oriented ideas during the prehistory of OO. I am not sure how they went about that, but the idea that closures can be used to implement data encapsulation in objects (that’s essentially what the code above does) has been around for a long time. With a little bit of work one can add inheritance through delegation.

If you prefer, you can iterate over the data using loop/recur. You miss out on some brevity and convenience, but in the end it should be a matter of personal preference. (I admit that that won’t help you understand others’ code, though.)

I know that the map function concept is unfamiliar to many people, but maybe it’s not that mysterious once you get used to the syntax. It’s just a little bit higher-level and less flexible than a for loop, or loop/recur in Clojure. It’s not a Clojure thing, either, except for the lazy output. It comes from Common Lisp and Scheme, which also inspired related functions in other languages. I used to use a map in Perl, for example (though I admit I had to worry about whether coworkers would understand the code). There is apparently even an analogue in Python.

fwiw, I almost never use thread macros. I guess I’m unusual, and I’m not a full-time Clojure person, but I’m usually happy with a chain of function calls or sequential let bindings, rather than having to think about the hidden arguments that are either passing through a thread macro chain as the first argument, or as the last argument, or that have to be jiggered into some other argument along the way.

I didn’t expect that. My point was that the problem wasn’t that I already had the tools to solve the problems and just wasn’t using them right. I hit walls because I don’t have enough tools to solve the problems. In other languages, the first tools you learn are enough to solve a much wider range of problems.

That’s my point. This whole thing started with people trying to claim Clojure is as easy to learn as any other language, which is empirically false. There may be various reasons for that, but it’s not an easy language to learn.

I’m not talking it down, I’m pointing out that it’s not as easy to learn as everybody wants to claim it is. It’s not a perfect, holy grail of languages. It has its perks. I’m learning it for a reason. I have my reasons. That doesn’t mean everybody will share my reasons and it doesn’t mean choosing a different language or paradigm is any better or worse. OOP is a perfectly valid and useful paradigm if used correctly. So is FP. It’s a matter of personal preference. Claims that Clojure is empirically superior in all these different ways simply doesn’t hold up. That’s not talking down the language. That’s reality.

I headed to Hackerrank for that. Been through that already. I know the language enough to solve basic CS stuff. I’m now trying to port my personal projects over.

2 Likes

Is this due to Clojure, or just any FP, or maybe just Lispiness? I did not feel a steep curve, but I had been doing a lot in Common Lisp recently (with a lot of imperative code), and had used Scheme, Standard ML, and a tiny bit of Haskell years earlier. Maybe one should expect a steep learning curve coming from those languages. (Well, I did get bit by laziness repeatedly, and I had a lot of trouble, at first, learning the right way to organize files for Leiningen and the JVM, and JVM stackdumps are not very friendly.)

I would never suggest that any functional language would be easy at first for people primarily used to OO and imperative programming. I think I’ve come across people who seem to pick up Clojure or OCaml quickly starting from OO, but I would not have assumed that’s the norm. So if that’s what people are saying–that Clojure is easy–I won’t disagree, but I don’t really know.

I say Clojure is easy, probably because I mainly use Foxpro Desktop database programming before using Clojure, and I’m used to programming based on data sets. In FoxPro, programming using dataset commands is simple, clear, short, and high-performance.

So I had used VB, C# programming, I had Learned also C + +, Delphi, Java, Python, asm, etc. Their grammar, implementation mechanism, programming ideas are too complex, the code is too verbose. I’ve always thought they’re very difficult to use.

So, about 2011 years, as soon as I saw a Clojure Chinese introduction article, I like it, although my English level is not good, Clojure’s documents and books are very few, there is no Chinese documents, I still use Clojure in my project.

For me, Clojure is a super Foxpro.

Clojure able to fully express my thoughts, so that my programming ability has been greatly improved, and let me focus on problem solving rather than programming language.

Clojure is a functional programming language based on relational database theory :smile:

3 Likes

Can you give some concrete examples? (so many things can be built from reduce – including map and filter – so I’m having a hard time imagining that you “couldn’t” solve problems given a pretty small toolset that you say you fully grasp)

I have not used Hackerrank, so I cannot comment on that.

The most useful feature of 4clojure is that you get to see other people’s solutions after you solve a problem, and you can also follow people. Following a few known good Clojure programmers, one would know the majority of the useful functions and idioms. At that point, I can hardly imagine how one would “hit a wall” using Clojure to solve problems after he has gone through a period of rigorous exercise like this.

There are less than 100 core functions that one needs to know by heart in order to become a Clojure master. Most of the functions match a specific conceptual “shape” of the problem and the name of the function is often very suggestive. E.g. map is a transformation from seq to seq; iterate is applying the same function on result and keep going forever; and so on. Intuitions like these are what one obtains through exercises. One cannot claim to have grasped the concept of Clojure without being able to immediately conjure up an appropriate function upon hearing the problem description.

I think @Richard_Heller doesn’t want to be convinced about the benefits of Clojure over Python. If you have a strong Python background, it’s normal to find Clojure harder to grasp. I came to Clojure after having done some Haskell, so the functional aspects were not a big stumbling block, like they seem to be for him.

If you’re starting from a blank slate, though, I don’t really know that Python would be any easier to learn. There’s a bunch of OO-related knowledge that needs to be grasped, and that usually just piles on top of the inherent complexity of the problem one is trying to solve.

Both Lisps and FP take a while to “click” for people with an imperative/OO background. You need to “unlearn” a bunch of stuff (some of which are actual anti-patterns), and start thinking in terms of data being transformed.

@Richard_Heller, if you haven’t already, you might want to have a look at Graham Hutton’s paper on the universality and expressiveness of fold, to better understand how to see things in a more FP manner. (http://www.cs.nott.ac.uk/~pszgmh/fold.pdf )

1 Like

Not true. I don’t use Python, I don’t really like it as a language. For ease of understanding and learning, though, it’s tough to beat.

The empirical data says otherwise. If too much OO knowledge is the problem, then beginners and non-programmers shouldn’t have a problem. When was the last time you heard a non-programmer say they started using Clojure because it was so intuitive and easy to use? Or a non-programmer who needed to throw together a program to test a theory using Clojure because it just made sense and took no time to learn? Those are the reasons people turn to Python. I’ve never heard anybody say those about Clojure. Steep learning curve? Frustrating? Those things come up all the time with Clojure.

So, the question then became why? Why does it take so long to get over the hump? I now believe it’s due to a lack of primitives and the IoC nature of it. Map is not a primitive. It loops, calls functions, and modifies data. 3 different things. And you have no control over it, you set it up and hand it off to the Clojure runtime. When starting out, that makes it harder to figure out why things went wrong. Yes, you have to figure that stuff out in order to really use Clojure. I believe that’s why it takes longer and is confusing when starting out.

Then the questions about why would somebody switch to Clojure over another language? What would that brochure look like? There’s not really anything outside of personal preference. That’s just the way it is. The REPL? People just hear “hot loading debugger.” The feedback loop for iterative development isn’t a pain point at all, let alone enough of one to justify switching languages. OOP vs FP? That’s personal preference. A lot of people prefer OOP. For Clojure to gain wider use, it needs to solve better problems. As is, it’s no different than talking about Rust, Haskell, Go, etc. Another general purpose language that some people prefer using. That’s not a bad thing. That’s just the way it is.

I’m not trying to knock the language. If this was a Python forum and the gurus were trying to say that Python code was comparable to C code for performance or something, I’d call bs on that, too.

Reducing the preference of many experienced programmers for functional programming into a mere personal preference does count as talking down the language.

For it basically dismisses out of hand any technical arguments these people make on behalf of Clojure, namely what their chief spokeman, Rich Hickey, have elaborated over the years. The truth of the matter is that nothing you said here contradict with what Rich Hickey said. He said “Simple is not the same as easy”. So you were basically attacking a strawman that you conjure up to make yourself feel better.

The focus of Clojure is on simplicity. Apparently you have nothing to say about that. That is OK for a beginner. But it is borderline ludicrous for such a beginner to claim mastery of the concepts of Clojure.

You may not realize, but your lament of “lack of primitives” is precisely the point of a higher level programming language. Lower level languages provide just three primitives that is required of a Turing complete language: assignment, conditional and loop. Clojure provides you with more and higher level primitives, such as map, filter and so on. It has to be so in order to become a higher level language. A higher level language is just more productive for whoever has mastered it than lower languages, because the higher level primitives are closer to human conceptualization than machine-like conceptualization. For a person who have internalized the machine-like conceptualization of computation, here lies the difficulty of learning because one has to unlearn the habits and patterns of thought of machine-like computation. For a person without such burdens, there may not be such a difficulty, hence some people’s claim of Clojure’s easiness to learn. The jury is still out on that one, but many anecdotes of hiring junior developers who are fresh out of college, indicate that there’s some truth to that.

Another counter-intuitive truth about programming languages, is that the higher of the language abstraction, the fewer freedom they give you. Structural programming language takes away your “goto”, functional programming language takes away your assignment. In return, you are given simplicity. It takes a lot of real world experience to appreciate the benefits of simplicity though, so it is not really a selling point for beginners.

3 Likes

If anything, I’m the only one on here agreeing with him. That’s been my basic point this entire time. It’s not easy. I agree that once you get it down, the simplicity of it is nice. I wholeheartedly disagree that’s as easy to learn as languages who’s stated goal is to be easy.

Y’all are confusing the concept with the implementation of the concept. Map is not a concept. The concept is to call a function on each item in a list. Clojure calls that concept map. I’m perfectly comfortable with the concepts, I use them in every language I use on a daily basis. Where I’m at right now with Clojure is not having memorized the 100 different names it gives to the different concepts.

Again, exactly the point I’ve been making all along.

2 Likes

Not true. I don’t use Python, I don’t really like it as a language. For ease of understanding and learning, though, it’s tough to beat.

IFF you have been exposed to imperative programming first, or you’re being taught that way.

The empirical data says otherwise. If too much OO knowledge is the problem, then beginners and non-programmers shouldn’t have a problem.

What empirical data? Most beginners are taught some imperative/OO language when starting out, so they think that way. Some unis (even in non-CS related fields) start out with FP, and their students learn with no further complications, too.

Map is not a primitive. It loops, calls functions, and modifies data. 3 different things.

This contradicts your previous statement about having grokked the principles of FP, I’m afraid. Map does not modify anything. It doesn’t loop, either, it recurses. Also, it’s not out of your hands, whatever you mean by that. You can compose it with other functions to control when and where the mapped function is applied, and for this, you need to simply understand the underlying concept.

It really feels like you’re just stuck in the imperative mindset, and are crapping over the language because you find it hard to fit in your mental model of programming. It is hard to change the way you think about programming (been there, done that), but there’s nothing inherently hard about FP (or Clojure) in and of itself. It really is a simpler language than Python, if you don’t come to it with a previous bias.

Peace out!

I would like to see the statement “It really is a simpler language than Python” pursued with Rich’s definition of simplicity (= not complected/braided) kept in mind. How do the core libs of Python vs Clojure compare, for instance?

It’s not necessary to memorize all or most or many of the functions that are useful in Clojure. I don’t know them all. I don’t even necessarily remember functions I’ve used before, if it’s been a while. That’s what clojuredocs.org, or the Clojure cheatsheets, or parts of clojure.org are for. If you’re disconnected from the Internet, find-doc, apropos, and doc in the repl are reasonable substitutes. Over time, I have gotten a sense of what sorts of functions are available, and I can then go looking for something like what I want. Or sometimes I just write my own custom version of what I want.

I just wanted to give my 2 cents without answering to anyone in particular. I happen to know Python very well, though sometimes I feel like I only scratched the surface, while with Clojure - though I learned it more recently - I know it: once you understand the basics of it you’re done.

Yes, you’re left with transducers and core.async, but the formers are already being ported to other languages and the latter, well Go is basically just it. So, after you understand these concepts as well my claim is that your Clojure code can actually go faster than regular Java (and especially Python) code. Why do I say this? Because with Clojure I don’t need loops, I just build transducers and then stuff them into a pipeline-something and that’s it. Boom. Parallel/Async processing.

Now try doing that with Python. Good luck. Asyncio is the craziest thing ever come out and makes everything so complex you want to simply pull your eyes out of your head and multithreading is very poorly supported. I have to add that I’m a data scientist/engineer, so usually my code is performance sensitive and with Clojure I was able to find the right tradeoff between performance, ease of development and maintenance cost.

Now about simplicity. Take at look at this: https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/svm/base.py. If you can understand this code ins and outs in less than 30 minutes either you wrote it in the first place or you’re a freaking genius. Python isn’t simpler than Clojure, it’s just that we’re used to stare at very simple scripts, but as soon as you have to get down to the serious stuff, well, so much for ease of use.

What the heck is ABCMeta? What about @property? Does it read as english? ‘At property’??? Is it a comment?

Well, ABCMeta is a class that you have to import from a separated module and use it to create abstract classes. @property: first off, it is a decorator. Guido didn’t like f(g(z(x))) notation, so now we have decorators. It is just a wrapper function called in a different way. Yes, it is a wrapper function that you use in methods inside classes definition to deal with properties and that returns new objects. That’s easy!

The point is that as soon as SLOC increase every language gets more and more complicated, by how much is the real matter. As an example, if you’d like to contribute to a library as complex as scikit-learn you need years under your belt of Python development, and for many it would be too overwhelming to do anyways. In Clojure, after a few months of development you can contribute to Incanter without many problems, though it is a much larger library in scope than scikit-learn (take the libraries as examples only for code complexity, to really contribute to these one needs expertise in data science and machine learning as well).

So Python is much easier to learn and to use for very simple stuff, it is almost unbeatable. For more serious stuff, good luck.

7 Likes

Recurse is a form of looping, which is why it tends to be written as loop/recur… If map doesn’t modify anything, what’s the point of calling it? To spin CPU cycles? It’s taking the results of the function you give it to call and modifying a sequence under the covers. And by out of your hands I mean that when you write (map f col), your code never directly calls f. You hand it to the Clojure runtime and it runs it when it thinks it should. You don’t control that.

Anyway, a couple parting words before I’m out. This has been a fun discussion but it’s been going in circles for a while now. First, the original question was how to sell Clojure to Python developers to get them to consider switching. To which somebody said the ease of learning is a reason. To which I responded, um, no it’s not. Then everybody flipped their lids, finally settling on “it might not be easy if you’re coming from a strong OO background and a language like Python.” Well, guess what? That’s the demographic we’re talking about.

Aside from that, just in this thread alone, how many people gave their Clojure stories and had it start with “it made no sense, I felt like I couldn’t do anything, until it finally clicked one day.” When I point out that’s a very common initial experience for people of all different experience levels, an experience that other languages don’t tend to have, do people look at that and say, yeah, maybe there needs to be better documentation, tutorials that are kept up to date and actually work, better tooling? No, the response is “You’re only having trouble because you’re brain is tainted with OO residue, you have no idea what you’re doing, and you haven’t memorized a bunch of cryptic functions yet. One you do that, it’ll be simple.” OK…

Giving an unbiased, honest assessment of the hurdles facing wider adoption of the language isn’t crapping on the language. It’s being honest about it, seeing it for what it is, and understanding how the software industry works. Clojure was designed to be a parallel processing language. That’s its strength and where it excels. That’s also a niche market that’s been shrinking thanks to microservices.

I still stand by one of the first answers I gave to the original posted question. The way to get people to switch is not by putting together some PowerPoint slides and discussing it over lunch. There’s nothing on paper that’s going to be compelling enough. You have to just start using it. Don’t ask permission, just do it. If you’re trying to get a friend to switch, start a small project with them that uses it.

So, I’m out. It’s been fun. Keep on keeping on.

6 Likes

Guys – let’s change the tone here. Personal attacks and slights are not cool and should not be part of the Clojure community. @Richard_Heller, thanks for putting yourself out there with what was evidently an unpopular opinion. Despite some of the cringeworthy responses, you have contributed to a conversation with many insightful perspectives, whether I agree with all of them or not.

9 Likes

@Richard_Heller, thanks for the thoughtful comments.

This thread has been going on for a while; I understand leaving it behind.

One comment if you’re interested. I think there may be a misunderstanding about map. Its semantics are clear. map takes one or more sequences, and returns a new sequence. map (used with two or more arguments) does not execute in parallel. (I’m not talking about transducers, which are a different, more advanced topic.)

What is kind-of sort-of out of the programmers control is that the sequence returned is lazy. So the execution of the function f may or may not happen immediately. This does make things more complicated, but it’s not arbitrary, and it’s not up to Clojure’s discretion. There are rules, you can reason through when the execution will take place, and you can force it to occur with doall.

(I do think that laziness makes Clojure harder to learn. Note though that the general lisp map/mapcar concept has nothing to do with laziness. Lazy sequences are just a general feature of Clojure.)

You can get parallel execution, though, if you replace map with pmap; then the way in which the input sequence is split up and the function is applied on different processors is out of the programmer’s control, but pmap is not a basic function; it’s not something anyone would be expected to learn early on. (I think it’s used rarely, though I personally love it.)

It is conceivable to me that in some cases the new sequence created by map will share structure with another sequence, but I think that’s unlikely, and it would not affect the behavior of a program.

Unless I’m mistaken, @porkostomus learned Clojure as their first language. Perhaps they would have insight to share about learning Clojure without prior language experience?

1 Like

Agreed. The topic at hand was inviting of diverse opinions. I think @Richard_Heller 's perspective was very insightful, and we’d be wrong to dismiss it as a community. I also appreciated all of the responses to that opinion, and the diverse perspective.

Lets focus on keeping things into healthy discourse, and not make them into debates. I don’t think anyone needs to be convinced one way or another, we can instead focus on simply understanding people’s angles, and opinions, respectfully, even without necessarily agreeing with them.

I also understand the people feeling a little challenged by the topic, and I think that’s also an okay feeling to have, this is a Clojure forums, full of people who love Clojure, hearing criticism, especially which we don’t agree on, isn’t always very nice.

Personally, I do think we should recognize a few things here:

  1. Some people do find Clojure easy. That’s the case for me. I did not find it hard to learn, I found it quite easy to learn, and I learned a lot of new ways to model software through it, which I found quite simple and elegant.
  2. Easy to learn, does not necessarily mean quick to learn. Clojure is built on top of an unfamiliar foundation, and each of its layers generally comes from more niche, less familiar theoretical basis. Learning it is like going back to school CS 101, first year, first lesson. This can be frustrating, especially to someone with already a lot of experience under their belt. Even worse, even the foundation of the foundation is unfamiliar, so some of the things you learned in elementary school and high school as your de-facto instinctive foundation won’t apply. So in some instances, you even have to go back to elementary school. Such as the prefix vs infix operators.
  3. Unfamiliarity, having to go unlearn the basics, and re-learn them over again, can feel painful and tedious. Which might make it feel hard to learn. It is unclear here, if it is any harder then the traditional foundation was to learn. But, in some ways, that doesn’t matter, since it is not what elementary, high school, and standard CS programs, books and tutorials teach you. That means unfamiliarity will almost always be a property of learning Clojure, both for non-programmers just getting started, as well as experienced programmers, without prior knowledge in most of Lisps, FP, lambda calculus, persistent DS, lazyness and concurrency primitives such as threads, futures, stms, csp, etc.
  4. Some people also find it very hard to learn Clojure. You can’t argue against that. People feel the way they feel. The question is, what can we do to help ease that feeling for them, as a community, in order to be more inviting of a more diverse set of people. @Richard_Heller is actually correct and on point in saying that Python will attract these people, where Clojure won’t. Now they suggest that it might be inherent to the way Python is, which makes it intuitively easy, where as the way Clojure is makes it intuitively hard to some (maybe even most) people. I can’t say I agree with that, but I also can not deny the possibility. Either way, I think it is clear that Clojure is not willing to change itself inherently in order to change this. Python on the other hand, has a tenet, and a goal to do so. But that does not mean there is nothing we can do to help. Better tutorial, more material, a stronger support group, access to content, all that will for sure have a positive impact on helping people overcome that initial hardship, no matter what the cause of it is.

On that, I’d like to leave new users learning Clojure with a few final notes:

  1. If you find Clojure hard now, do know that it gets easier. The struggle disappears, and everything starts to make sense, and becomes really fun and pleasant. There’s no proof to this, only lots and lots of anecdotes from people who went through that same journey.
  2. But, there’s no reason to struggle to the point of exhaustion and total frustration. Put it aside, and come back to it much later.
  3. If you still find it hard, you most likely just havn’t gotten it yet, and done enough. I’ve mentored many people to Clojure professionally, and almost everyone one of them complained and moaned for the first two to six months (of full time work). Yet now they have Clojure stickers on their laptops, and can’t stop talking about how awesome it is. By the way, 2 months is 320 hours, and 6 months is 960 hours. So if you havn’t put anywhere near this much time, it might be normal that you still struggle.
  4. And maybe you just don’t like it. At the end of the day, use a tool you enjoy using. I think one thing that the Clojure community can pride itself in, is that we don’t downplay other languages (except Java :p). There’s trade-offs to Clojure. Part of those is why Clojure is an incomplete language. The ugly parts we leave to Java and C# and JavaScript, and recognize it is still best to use them for it. By nature, Clojure recommends polyglot knowledge of programming languages, where you are expected to learn at least Clojure + 1 (either Java, JS or C#).

And remember to have some fun! :partying_face:

8 Likes