Why Clojure over Python?

Thanks for those comments, @didibus.

I keep thinking about The Little Schemer (née The Little Lisper). There are Clojurifications of it on the web. It’s a weird book. There are quirky questions on the left, and answers on the right, and you are supposed to cover up the right side while you think about the left side. You don’t even need a computer, although it’s nice to experiment at a REPL, too. I don’t think most programmers would be willing to work through it in the way that it was intended, and maybe they don’t need to do so. However, when I was a fairly inexperienced programmer, but had been studying Common Lisp on and off for a while, working through The Little Lisper was what, for the first time, allowed me to grok recursion.

The book would not be enough to properly learn Clojure, even rewritten in Clojure. I wonder, though, whether some sort of style like that could help people overcome the initial hump to Clojure programming for most people, if they were willing to sit through the process. The Little Schemer style is so slow, and so simple and friendly, that I have the feeling that it could overcome any countervailing tendencies, if the reader were patient. I don’t know.

@eccentric_j I’m not sure how much I’m able to contribute to this discussion but it does seem quite fitting right now because, before choosing Clojure I did have a few months of Python experience, and recently have mostly gone back to Python for a couple of reasons.

As far as the language itself I prefer Clojure, hands down. I chose it in order to learn what I recognized as the most effective programming habits, and absolutely love everything about it.

Clojure as a (nearly) first language has been really interesting. I find that the things most often complained about Clojure being “hard” do not seem so to me at all, because I guess I lack reference. It’s all new.

The difficulty that I’ve been encountering with Clojure is finding work, since most other applicants are way more qualified than me. So I’ve been spending time getting other languages under my belt to round out my portfolio.

Besides experience programming, I’m also lacking even a basic math education, so a few months ago I began teaching myself algebra while using Clojure to assist me by writing functions to perform the computations and putting together notebooks as web pages with KLIPSE snippets. While trying to write my own solvers and whatnot, I thought to look at other languages that are more focused on symbolic computation to see how they did it. First I used Wolfram (Mathematica) and fell in love with the notebook interface, but not its proprietary model. So then I found Jupyter notebooks, and have been happily using them with Python ever since. I know that there is a Clojure kernel for Jupyter, as well as other live notebooks, but for now I’m having a great time exploring the Python data science stack, even though I don’t particularly like the language!

I’d love to be able to just use Clojure for everything, but right now I just need a job. I started doing all this 3 years ago in order to get myself off the street, and still have yet to write any code for money so my situation is a bit… desperate. It occurs to me that if finding immediate entry-level employment is the goal, trying to do that with Clojure may not be the smartest choice.

4 Likes

I actually started writing one of those! That’s how I got my nickname, “The Crazy Clojurian”!

1 Like

Thanks for sharing your experience @porkostomus. To some degree I think you’re a bit exceptional with your drive and studious habits, I still kill time with your Minesweeper rendition from time to time. However your experience is a valid counter-point to over-thinking how difficult learning Clojure as a first language actually is (something I’m guilty of): It’s at least possible!

Topic detour:
If you did want to give a Clojure notebook a try, Next Journal and Maria.Cloud seem really solid:

https://nextjournal.com/a/KT71s88aKdZdDgu8xRzp6/edit
https://maria.cloud

Additionally I drafted a small library to turn clj files into notebooks:

Back on topic and not addressed to anyone specifically:

I actually failed learning Clojure the first time. I had just begun grasping functional programming and had a friend who was really into Clojure a few years back that made me think it was worth trying. Within the first few chapters of Brave & True I found myself more and more frustrated – it just didn’t make sense. I liked the syntax but the solutions just were not something I would ever arrive at myself.

Then I spent about a year or so studying functional programming in general and applying the techniques in my day-to-day JS. Eventually I wanted to find a language that was designed with functional programming principles in mind where as JS will always take a strict amount of discipline from every developer that touches our codebase to keep it cohesive. That’s when I found my way to Clojure again and remembered I had a copy of Brave & True. This time around I had no problem transitioning! I had incidentally learned most if not all the required concepts to understand the solutions in Brave & True and was able to solve the exercises I attempted.

That said learning Clojure as a programming language wasn’t any more difficult than any other language I’ve wrestled with. In many ways it felt easier not having to memorize a bunch of arbitrary symbols and their order of importance. Learning to think functional however, that was a bit of a mountain, it took some will power to want to relearn everything in a different paradigm – almost starting from scratch with the baggage of my previous experiences weighing me down. Early on it was easy to get stuck with Clojure and think, “Ugh. I could solve this so easily in JavaScript.” Now when I use JS in my day job I think “Ugh. This could be made so much more simple in Clojure”.

4 Likes

Where was that?? There was a post saying Clojure was kind of easy after working with FoxPro. I, for one, was not steeped in FoxPro and the first little while with Clojure went somewhat as you described. To put it delicately, I really would not suggest that the untutored getting-started experience is the selling point. But the throes passed. And that’s it, in a nutshell: the throes pass! Before Clojure, I had used only languages where the more you know, the harder they get.

Hi everyone,

Thanks for having this elaborate, considerate, in-depth discussion. This really is what ClojureVerse is here for.

Comparing two different technologies is often a little contentious, and we’ve seen in the past that as these discussions drag on the level of discourse tends to drop. People start impulsively replying to specific turns of phrase, without keeping the full context of the discussion in mind, which admittedly becomes hard when there are over a hundred preceding posts. This is also the reason we auto-close topics. At a certain point we have to agree that the discussion has been had.

I’m not closing this topic just yet, but I’d like to ask people to show some restraint. Please consider carefully before posting whether you’re bringing something new to the table, or if you’re reiterating points already made.

Please also keep in mind that on this forum we value a friendly environment above everything else. When someone has an opinion that wildly differs from yours, then make an effort to understand why a person would come to such a viewpoint. In what context, and with what background would you come to this wildly different conclusion? This is how we learn from each other, and is infinitely more fruitful than trying to change the other person’s mind.

In the end we also have to accept that everyone has some bias, and that even discussions about technology can never be 100% fact-based and objective. Rather than becoming defensive when our beliefs are challenged we should use this as an opportunity to examine our own biases.

I found a lot of good food for thought in this thread. Thanks @Webdev_Tory for starting this discussion, and @Richard_Heller, @didibus, @mars0i, and the other two dozen contributors for sharing their insights.

9 Likes

I might have an interesting perspective here, since I am not a programmer by training or trade and have kind of glued together a bunch of stuff to do what I find useful or interesting.

I started with imperative programming, both Perl and Java, so I also struggled at first with Clojure. I’ve been trying to think about why, and I think that part of it was not really knowing where my data was. In an imperative language you define a variable and then you modify it, so I feel that, no matter what is going on in my code, I know what my data looks like and can output it easily and examine it. Of course, that can be a downside too; I don’t know how many times I accidentally incremented the wrong variable because I had forgotten to change a variable name somewhere (that might not happen to experienced programmers). So when I started with Clojure it was a struggle at first to understand that my data is, you know, in the function. So how do I get it out? Sometimes I still forget that I can view the first few calculations in a function by “take”-ing from a lazy sequence.

That said, I’ve tried to teach two sets of non-programmers in an imperative style and they have a lot of trouble with Python. My wife once asked me why you would define something to be zero and then keep adding 1 (or whatever) to it, instead of just adding a list of numbers. The latter approach is more FP, and she has found R to be more friendly because it has more of a nested function structure and she doesn’t have to remember where her data is (mostly). My non-programmer accounting students struggled with Python for similar reasons, and I also think that they might find a FP approach easier next time around.

For myself, I switched to Clojure when I found that my Java code was getting too big and clunky, so perhaps I finally crossed the complexity threshold that others on here have mentioned. I had a bunch of classes, and I couldn’t always remember what they did, or what I called the methods, or how to pipe them together. As I said, I struggled a lot with Clojure at first because it was hard for me to see inside what I was doing and I hadn’t really grokked the abstraction of it. Now I much prefer using Clojure, and recently rewrote a complex piece of ABM code, for which I was using MASON in Java before, in like an hour using only native Clojure functions.

I very much appreciated @Richard_Heller’s comments. I am surrounded by Python programmers, and I am not sure that I will be able to convert them. Most of them care only about getting something, usually relatively simple, done as quickly as possible. So they often don’t reach above a certain level of complexity. It is also easier for them to find Python code that they can cut-and-paste on SO or on the web, and they don’t particularly care how it is done. I am actually interested in the conceptual framework, and that is part of the reason why I am here (and why a non-programmer would watch Rich Hickey lectures on abstract CS concepts that have little or no practical application to what I do day-to-day).

8 Likes

Excellent thoughts; I particularly like your exercise of going over your early learning experience. I also began with Java and Perl (and some C, Javascript, and PHP too). I’ve not thought deeply about this, but will give it some thought and see what I come up with. I suspect my journey reflected more the fact that I learned about Lisp long before I learned about functional programming.

This is the two most important reasons why I like Clojure.:

  • Simple and smooth data manipulation.

  • Simple, flexible S expression syntax, zero syntax learning costs, it can focus on work, not subject to syntax and its upgrade interference. It’s like a Markdown in the programming language world, but it’s surprising that Markdown is popular, and S expressions are not popular,

    • Is S expression simpler and too more expandable than Markdown?
    • Or is the C-like syntax habitually too strong?
    • Simplicity is the ultimate sophistication.

:smiley:

1 Like

I somewhat share your experience, I myself am a self-taught coder, but I started with R, then moved to Python and finally to Clojure (leaving aside languages I use much less now). R is really a few steps away from Clojure or another LISP and that experience has helped me a lot with learning Clojure.

1 Like

I decided to learn Clojure after having learnt Objective-C (I wanted the C preprocessor to be able to handle C itself as a macro language) and was already into quite advanced programming stuff (last thing I did in Objective-C was a library to code in an aspect oriented fashion).

I started learning Clojure straight with 4Clojure challenges, no book, just a cheatsheet of common Clojure functions and apart from having to learn how to use basic items like let from the problems’ solutions which was a little disorientating at first, the biggest adaptation I had to do was to think functionally, i.e. with code flow explicitly in the form of a directed acyclic graph.

At first, I literally had to draw the graph prior to writing code, then I quickly got used to thinking in these terms. Eventually I learnt about libraries like Prismatic’s Graph that explicitly treat and present code as a graph.

This is what I love with Clojure (and what somehow prevents me from using other lisps): the flow of syntax matches the flow of data most of the time. To put it in other way you have several “domains”, that of unexpanded code, that of code, and that of functions, with steps that functionally link one domain to the next one:

unexp. code -- [reader-macros + macro-expansion] --> code -- [compilation] --> functions

The items in these domains are linked through a directed acyclic graph (respective to each domain). Code, both expanded and unexpanded, is tree-ish. Ditto for functions. You have a many-to-one, branches-to-root, inputs-to-output underlying structure to both.

Now what’s interesting is that transformation between these domains preserves order relationships between elements from the source domain to their counterpart in the target domain so that you can trivially reason on order relationships between items from distinct domains.

  • A macro can only modify code that is <= to the processed form (&form): it cannot transform outer code. This is due in part to the fact macros are functions under the hood.
  • Side-effects excluded, data transformations only happen downward in functions too: a function cannot change a variable outside of its scope, and by scope I mean lexical, i.e. syntactically ordered, scope.

What does this lead to ?
To put it in other words, each step in transforming Clojure code and data, from reading it to compiling it, as well as “mutating” it (i.e. creating a new version of it), can be seen as graph transformations on “structured” graphs (i.e. DAGs), specified as DAGs themselves.

And this is really interesting because this is the shape of causality. I often hear that no matter what, FP is implemented on imperative bases. However true this may be, an imperative program where cause and effects are fully tracked and analyzed will take the shaped of a DAG.

3 Likes

I respectfully disagree. Superficially, if you just look at syntax, what you wrote is true. But if you consider building real systems with it (especially on a larger scale), Clojure offers a lot over other languages/ecosystems, and I get a lot of significant performance improvements if by “performance” you mean building real working large systems with a small (in my case, one-person) team. If you consider features like core.async, transducers, immutable data, built-in sequence abstractions, and ClojureScript (the elephant in the room), you can no longer equate Clojure to “other languages”, simply because there isn’t anything quite like it.

As to advice on learning/working with a language and finding jobs, I would always suggest to learn as many languages/ecosystems as possible, to broaden your views. Get the “Seven languages in seven weeks” book and work through it. Then get the “Seven more languages in seven weeks” book and work through it. Then write applications in some of these languages, which will start giving you a feel for how they work in practice. Do not box yourself as a “Python programmer” or a “Clojure programmer”.

4 Likes

“Stop treating beginners, junior devs, and all devs in general like they can’t learn hard things and learn to ride a horse, even a really high one"

I can’t agree more with Didibus.

To me the most problem in our tech-industry is the short-term perspective of projects.

In this way, everybody want to pick-up the most easiest thing for doing results in short-term, which leds to throwing away Products every 1/2 because they becomes unmaintainable.
E.g the python 2/3 migration history, in legacies systems is really hard to maintain.
I picked up python, but one could do the same for other lang (golang/python/ruby/scala) all this which introduce massively breaking changes.
(speaking from experience)

And I while I agree to

Stop treating beginners, junior devs, and all devs in general like they can’t learn hard things and learn to ride a horse, even a really high one

I also agree with

All devs are not equal, and under the right conditions, one can be 2x, 3x, 4x … more productive than another one. I’m not even mentioning the 10x shorter 10x as powerful lisp program you can write in 10x less time.

So @Richard_Heller, why not pay them really well, foster their talent and encourage them to start their own business and finance it, in order to have a different kind of turnover, one that is not seen as a leak to the Big Market from Outside but as an accumulation of knowledge, skills and code within a web of sister companies ?

1 Like

Another thing to note is that any large project can be broken down into smaller independent components. Small components are easier to reason about and maintain directly leading to higher code quality. It’s easier to onboard people to work with them, and you can reuse them for other projects. Smaller teams also have way less communication overhead because there’s less meetings, emails, and less need for coordination in general.

1 Like

Lambda calculus strikes a better balance between human readability and machine readability. And being able to reason about one helps us reason about the other.

Algo’s sales pitch against lambda calculus is, “hey look, our statements look more like spoken language. These various precedence rules are the middle ground between register statements and natural human speech.”

The idea was that by incorporating parochialities of human languages into each statement, we’d be able to bring programming to a wider audience. Parochialities of infix notation. Various other parochialities of operational order of precedence. Intuitions about how humans communicate, baked into the language - all while trying to hide the underlying datastructures that don’t really obey those intuitions.

The other argument for algo-based languages is that most register-based CPUs provide a mostly update-in-place architecture, using store and load statements that sorta-kinda look like really short human sentences. So there’s a short path between those and the illusion of spoken language commands.

Elixir is a good corollary example here - the syntax inherits from algo dialects, but it provides an easy pathway to homoiconically understanding how the compiler is interpreting the syntax. This is why you can do macros in Elixir. I’m betting most Elixir programmers know how Elixir works better than most Java programmers understand how Java works.

In the end, these languages are just different colors of lipstick over the same pig, right? I just find that I’m able to understand a language a lot better when I also understand the AST of it that the computer is working with. All the other syntactic parochialities are just maybe-conveniences for specific domains.

2 Likes

The only thing with indipendent or smaller components, is to make this component really indipendent from each others. Otherwise you have even worst pattern as monolithic repos. Where each small things depend on each other. But a part from that I belive that small is beautiful, either on team size and on projects. Projects with lot of devs are not effective and also really confusing in term of design

1 Like

Hi!

For anyone following the discussion here (must admit I lost track a bit :blush:) –

This Thursday we will have a web meeting about clojure<->python interop, introducing libpython-clj by @cnuernber and panthera by @alanmarazzi.

Indeed, the main orientation is data science, but I think that anyone interested in bridges across languages will find this meeting useful and fascinating. At least, that is how I feel after hearing a little about the speakers’ plans.

Anyone interested?
(would be great if you could register in advance)

(edited: link)

2 Likes

I have taken a LONG time away from programming. I have written about 3 TDD tests in C#. When I started to learn Clojure, I couldn’t understand how to do testing but it is conceptually much simpler. A function should return exactly the same value every time. If you change the code, has the value changed?

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.