That does tend to be more subjective, but given that it’s the reason for Python’s popularity they did something right that no other language has done.
Because elevators have a small number of things they do. The buttons could mean Open/Close door, Up/Down, or select floor. The symbol has to mean one of those. In a programming context, what could -> possibly mean? Iterate the possibilities. It could mean anything, depending on what the compiler writer wanted it to mean.
I’m referring to the complexity that all languages have in some form or another.
The adoption of Lisp isn’t only due to familiarity, it’s also the killer feature aspect. What do you get for learning Clojure’s complexity over another language’s? C++? You lose speed and low level ability. Java? At best it’s a lateral move and you lose speed, especially if your code loops a lot.
Clojure doesn’t offer anything over the existing languages other than personal preference. It doesn’t give significant performance improvements or make things significantly easier. There aren’t any pain points that go away by switching to it. On top of that, you risk spending a few years working with a technology that won’t help you find your next job.
I feel without being able to characterize the properties and patterns that make Python easy to read, then we are in a losing battle, in that we can’t work to improve easiness to beginners. I also feel Python is easy to read, but I’m hoping to prove it with reasons, not just impressions. For now, I’d say these are the characteristics that make Python easier:
the code is forced into a proper indentation and formatting
it has a very small surface area (minimal amount of features)
it has a lot of learning material
it doesn’t require you to deal with static types
I think there’s improvements Clojure could make in these areas. Auto-formatter seems to be a good one for #1, as well as better formatting support in editors, books, online material, REPLs, etc. For #2, maybe recommended approaches would help, so you don’t overwhelm people at first with the infinite set of possibilities on offer. For #3, obviously, more published tutorial focused on beginners, more access to material, making it easier to find, etc. Number #4 is pretty well covered in my opinion.
That said, I’m not sure Python’s popularity is due to this. I think being installed by default on Linux and Mac probably played a bigger role. As well as its focus on interop with C. To a lot of people, Python is a DSL for C APIs.
Well, if you refer to my post above, you’d see that I believe there’s quite a few concrete technical reasons to use Clojure over Python. Specifically to your comment:
It’s fun to use (that’s my personal favorite, so I’ll put it first)
It is the most performant and scalable dynamic language which is production ready that I know off. Much more performant than Ruby, JS or Python. So I’d say it strikes this balance of interactive high level programming and good enough performance to be used at scale and for large services.
It makes programming for the JVM easier than Scala or Java do.
It makes programming for browsers easier and more compatible across different JS runtimes than JS does.
It is full stack, front-end + back-end, and supports multiple VMs: CLR, JVMs and most JS VMs.
It definitely removes quite a lot of pain points, especially related to state management, OOP, and concurrency. It removes a lot of the common verbosity and repetitions that other languages have.
Or lets put it another way: Why not use Clojure[Script] ?
I still strongly believe the biggest reason is familiarity.
I’d agree with that list. Only thing I would add is that it looks like pseudo code. If you have an algorithm that you’re trying to implement, the Python code will look more like it than any other language.
It actually is. The competitor to it is Perl, which is still installed by default on Linux and has the C interop stuff. People prefer Python because it’s easier to deal with.
It hasn’t fully gotten there for me, yet. Clojure itself I’m weary of because of the looping issues. I realize that’s not really an issue for most projects, so I’m hoping if I come back around to it later it won’t matter to me.
Clojurescript has been frustrating to get up and running. The various templates are buggy and the docs all say stuff like “require [some-package -latest-version-]” but don’t say how to figure out what the latest version is. Now that I’ve got a working code base, hopefully it’ll start to roll.
I haven’t found that to be true. If you work in a Java shop for very long, you find out how great the tooling is around it. It’s pretty easy to code pure Java if you take advantage of the tools available.
I haven’t found anything that can compete with Vue on this one.
I’d say that depends on what you’re trying to accomplish. If you’re trying to code for a living, there aren’t enough jobs in it to justify putting the time in. Everything it does can be done in another language, so it really comes down to personal preference. If there’s another language you like better, you won’t gain anything by learning Clojure.
@Richard_Heller, the preceding discussion with @didibus covered many points I might have made, and I think some of your points are good.
The only thing I want to add is to push back on the claim that Python is English-like. I looked at several examples here. To me it’s not at all like English. It just looks like code. I can read it without much trouble, but I’m used to reading code in many different languages. When OOP became popular, I had to get used to C++/Java syntax, as in this Python example from the link above: self.valueChangeCards.append(card).
I don’t agree with this statement. I find that the REPL driven interactive development workflow that I get with Clojure is something that I have not found in any other language (except Common Lisp, but that language has it’s own issues).
I’ve done lots of Python development, and the REPL workflow is not comparable to Clojure at all. Being able to test new code against a live system, making changes without any reloading or recompilation necessary, makes programming fun again.
However, I think the problem with Clojure is that the benefits are not that obvious, because I think when most people start developing with Clojure, they try to use the REPL in the same way they would with Python or Ruby, and then can’t see what the big deal is. It is only when you have used it for a while and start not using the REPL directly, but only as a server that you send code to, that you start seeing the benefits of interactive development.
My personal feeling is that Clojure has completely different goals as Python. Python is optimized for the beginner programmer/non-programmer that just wants to write a quick script. It is easy to read because it is very similar to English/pseudo code.
However, once you start writing more complex systems, that same straightforwardness turn into shackles, and the code quickly becomes more and more complex.
Clojure, on the other hand, is optimized for more complex applications. It is more difficult to get started, but once you are building more complex systems the code is much easier to reason about and understand. And then, when you combine it with the interactive REPL based development, it is much easier to take apart and understand a complex Clojure application than a complex Python application.
There is definitely a place for both Python and Clojure, and I don’t think one is necessarily better than the other (although I have a strong personal preference for Clojure). Python is easier to get started with, but more difficult when your application gets more complex. Clojure is more difficult for the beginner, but simpler when you are building complex systems.
Hum, that’s true, but I guess this will always be a challenge for languages modeled after the lambda calculus, because people learn algorithms in a very imperative way, for the Von Neumann model.
Pseudo code of recursive and functional implementations of algorithms look quite a lot like Clojure, but that doesn’t help with making them familiar
That’s true, Perl is a good example. I’ll accept that we can most likely believe Python became popular at the detriment of Perl mostly due to its ease of learning for beginners and non CS majors.
What looping issue are you referring too? I’m not personally aware of any, so I’m interested.
I guess this is an opinion, but me and everyone I’ve seen give Clojure its fair share have found Clojure to turn out easier than Java, once they are past the familiarity hump. And that’s in a work environment I’m talking about.
I think you will actually learn a lot. I mean, once you know one turing complete language, you won’t gain anything learning another. Except, in practice, there could be technical reasons to use one over another (which I’ve covered before). And in theory, you will learn quite a lot of new concepts and modelling techniques by learning Clojure, obviously, depending on your starting point. If you’re coming from Python, I think you’ll learn a lot, and go back to Python a better programmer, and it’ll make your Python code even better.
I could see that. I haven’t done enough with Python to hit that, but I feel the same way about Node. Throw together a quick REST endpoint? Fine. But the more complex it gets, the issues with the language start to magnify. The shift to microservices makes it a bit of a moot point, since individual programs don’t need to scale anymore, but I can see Python having a similar issue.
I’ll try to clarify that claim. I’ll use calculating factorials as an example. If you were to explain to somebody in English, without programming terms, how to calculate the factorial for N, how would you explain it? Nobody would say, “Do reduce with the range 1 to N using multiply as the function.” It would be more like, “Start with N, multiply by N-1, and repeat until N equals 1.” So, take that and formalize it,
result <- N
result <- result * (N - 1)
N <- (N - 1)
until N = 1
Now you need to get a computer to run that. The easiest path, meaning least translation, from there to machine code is with Python.
I’d argue that Python isn’t easy to read because it’s imperative. C++ and Java are imperative but aren’t that easy to read. I’d argue Python’s easy to read because its flow more accurately aligns with how you’d think of things in non-programming terms.
I think the heart of it is the reflection and boxing that Clojure does under the covers. From what I’ve read about it “only twice as slow” is about as good as you can expect Clojure to get to Java on tight loops.
I didn’t explain that well, either. There’s things you could learn from any language. The things you learn from Clojure you could learn from other FP languages. If you happen to prefer those languages, there’s no need to learn Clojure.
Absolutely. It would really suck if we only had one language to use.
Python and other C-like languages, because of the customary factors, the surface is easier to get started, in fact, after the introduction of basic grammar, advanced grammar and its implementation is very complex. After getting started with beginners, it is difficult to be a programming master.
In fact, Clojure is simpler than other languages, easier to get started, as long as it adapts to its expression, Clojure just learn its four persistent data structures and corresponding operating functions, this is the introduction, After getting started, you can be a programming master, because all the clojure features are expressed by these four data structures, Operation of these four data structures is the whole of Clojure. All the Clojure knowledge is so simple, there is no difficulty, it’s the fastest way to be programming master.
I’d actually argue the opposite - the above describes a particular imperative procedure for calculating a factorial, which reveals an prior familiarity with how the resulting machine code would work.
If I were to explain factorials in English without programming terms, I would say:
“The factorial of N is the product of all integers from 1 to N”
“To obtain the factorial of N, reduce the set of numbers from 1 to N into a single number by multiplying all of them together.”
This explanation allows someone manually calculating a factorial to optimize the calculation by chunking “easy” combinations of numbers like (2 × 5) or ((4×3) × 12), taking advantage of mental shortcuts.
Similarly in Clojure, (reduce * (range 1 n)) doesn’t impose any particular evaluation order, making it possible to do parallelization under the hood, whereas the original imperative explanation ties you down to a linear sequence of steps.
I think such a declarative mental model is key to working in functional languages - whether it’s more intuitive is up for debate, but it might be something that people coming from languages like Python have to learn by “unlearning” imperative mindsets.
That’s the definition. What’s the algorithm? How do you get the product of all integers from 1 to N?
That’s the way to think about it for FP. You’ll never hear factorials explained that way in a math class.
Right, but that’s thinking in programming terms of how the computer will run it. I don’t doubt how useful the FP model is. But when talking about which language is easier to follow, it takes more mental hoops to follow Clojure.
That’s not true. The syntax is simpler, yes. But the syntax doesn’t do anything for you. Implementing the most trivial of algorithms in Clojure is a daunting task at first. You learn the basic stuff of map, vectors, maybe loop/recur, and then quickly run into cases where they don’t work and there’s no obvious way to figure out what does work.
The HTML table question I posted is a good example. This is the 3rd time I’ve implemented that code, the 3rd language with the 3rd HTML template system. It’s the first time I’ve had to ask for help with it. I tried every Clojure construct I could think of and nothing came out right. That’s not from a lack of understanding FP. Conceptually, I knew what to do. That’s from not having come across the myriad of functions you need to know in order to get Clojure to do anything. The problem is, if you’re not familiar with some of the functions, there’s isn’t a good way to reconstruct their behavior from other functions. You’re just stuck.
I was taught factorial in math class as a purely recursive definition: fact n = n * fact( n - 1 ) with a base case of fact 1 = 1. Pretty much all the math functions that are step based were taught as recursive definitions in my school, as I recall.
And of course the implementation is the same in all languages that support recursion (which is pretty much every one of them) but later, in comp sci, you learn that some languages have stack limits and only at that point were we given imperative, loop-based solutions.
In thinking about the idea that FP might be as natural as imperative programming if one learned it first, I have been thinking about the fundamental difference between mathematical operations and modeling things in the world.
In math, you normally do not change things. In x = 2 + 3, 2 never changes, 3 never changes, and x is just another name for 5. I didn’t use up 2 by using it in a calculation. It’s always available. There is almost never anything like repeated assignment to the same variable. Of course I can use x for something else, but it’s just an abbreviation. This is fact about math is, of course, a big part of the inspiration for FP. (There are exceptions, as in certain kinds of iterative maps in chaos theory, but in general, everything is reusable, and variables are just place holders.)
However, in the world, things themselves change. If I make an omelet, I turn an egg in a shell into a yellow liquid and some broken shell in the garbage. I can’t go back and reuse the original egg; it’s been destructively modified. Bags and houses get filled and emptied, things move, things decay with time. (If only I could reset my worn-out chair!) That objects themselves are altered is a big part of life, and so it should seem very natural to most people. This might make imperative programming easy to learn.
Perhaps imperative programming would be naturally easier to learn than FP for people who have less background in math. Perhaps for people for whom math seems very natural, though, FP would be equally easy to learn, and the difference in ease of learning a programming style would be merely a matter of path-dependence: Which did you learn first? But maybe not.
Ah, but if you factor time into your world, things are immutable: if you had a whole egg at noon and you applied the function make-omelet at some time after that, then you have a new world (based on time) that has added eggshell to the garbage and produced a yellow liquid for cooking. It doesn’t change how your world was at noon – you still had a whole egg at noon, and at noon your garbage did not contain eggshell.
I think that’s an important way of looking at the world: history is immutable – each action produces a new world at a new time. When countries change their boundaries over time, maps of those countries don’t mysteriously change – new maps are created instead.
In philosophy, there are views that treat state in exactly this way. e.g. tha what is true or false is always true-at-a-time, and therefore that P is true-at-t is the case for all time.
I write agent-based models (ABMs), which one normally thinks of as involving persisting agents (people, animals, molecules) whose states and relationships change over time–and they are normally coded in an imperative style. However, it’s possible to write an ABM in exactly the way your point about history suggests: Update the entire world and all of the agents at once, and then put them into a new world, leaving behind the old (or saving it in a sequence, allowing you go to back and examine the past).
I find I’d explain factorial as the multiplication of numbers from 1 to N inclusive. So factorial 5 is 1 * 2 * 3 * 4 * 5 = 120.
Now, here’s the deal. How you would pseudo code that depends entirely on your knowledge of computational models and their languages.
Pseudo code is just coding with terse syntax where you skip over details. You can’t do it without having learned a computing model and some of its common language constructs.
I’m not sure what someone who didn’t get any initiation to this would do if asked to pseudo code it, I think they’d just choke and not be able to write anything.
Basically, pseudo code is also thought, and that’s the beginning of familiarity.
So when we say, it looks like pseudo code, its really: it looks like the pseudo code I learned.
And I do believe most people are introduced a very procedural, structured, pseudo code model at first. Which makes sense, since real computers are imperative, and simple procedural structured programming tends to be close enough to it that there’s no performance loss, or difficult translation layers, while helping a lot with shortening and simplifying algorithm implementations.
That said, I actually see a lot of pseudo code doing very similar things to the sequence abstractions. And here would be my factorial pseudo code:
mult(1 to n)
Now, I need to be thought the concept of procedures or functions first to be able to write this. I’m skipping over how 1 to n would be implemented. And I’m assuming mult can multiply wtv 1 to n returns. Also, notice my syntax, its biased, why is that the syntax for procedure calls? Is it biased by math? Maybe. But its probably also biased by how I was thought it. More naturally, I feel I would just have done:
mult 1 to n
And why not:
(mult 1 to n)
I think most people would start with pseudo code like that. This translates quite well to FP and Clojure btw:
(apply * (range 1 (inc 5)))
Now, here’s where I’ll very much agree. Procedural structured pseudo code I do think on average looks like python in syntax and name choices. That might be the genius behind it.
Does Clojure look like functional pseudo code? I’m not sure. I think Haskell does more. It has that similar indentation based syntax which is more natural to pseudo code. But then, Haskell has worse names. Even Clojure you could say could do with better names and more pseudo-code friendly functions. Like * could work on colls, removing the need for apply. Or an alternate mult could exist that behaves as such. Maybe we need a range that can specify inclusive/exclusive so the weird inc isn’t needed. Or maybe range could allow decrementing ranges like (range 5 0). Etc. And maybe these are things Python did better for most simple common pseudo code of the imperative style.
It would be interesting to compare Python to JS, Ruby, Groovy, VisualBasic, etc. Is it really closer to pseudo code then all these?
Learning clojure is just a thought that requires a collection operation, combining collections like Lego bricks, just like the code I am answering your html table problem. I don’t understand why programmers like the OO method of attribute-by-object-by-object. Although it is as simple as elementary school mathematics, it is inefficient, clojure is simple and efficient based on the collection of higher mathematics.
That creates some data that is created when the function is called and is tied to the function that created it. That concept is the founding cornerstone of OO philosophy, i.e. the data and functions/methods using and manipulating it are tied together into a self contained unit. Granted, the OO people took that and ran with it a bit further than is really necessary, but that Reagent code is a perfect example of OO design being used in an FP language.
As far as the efficiency, there isn’t really anything to back up FP being more efficient. C++ is plenty fast. Apples to apples, Java outperforms Clojure. Hickey even stated he expected that. Where Clojure has an edge, at least out of the box, is doing things in parallel. It doesn’t need to do locking, which means less operations and faster code. You could mimic that behavior in Java, but that’s not how it works by default.
Thanks for your code. I’ve been reading through it trying to figure out how it works and how I could create something like that myself starting from nothing. I’ve been trying to pull it apart to see how each of the Legos works and what each one is doing. That hasn’t been the simplest of tasks, because it’s pretty tangled, but I like the idea of having a completely different approach than just for loops.
I’d say yes and it’s mainly because of the white space thing. Other languages have delimiters and blocking syntax that you wouldn’t write in pseudo code.
“Call a function that does the work for you” isn’t really good pseudo code, though. It does seem to be how Clojure expects you to think.
Which brings me to the epiphany I had last night while I was once again beating my head against the wall trying to get a web page written that should have been 30 minutes of work but instead is still incomplete after 4 days. When you read about peoples experiences learning Clojure, why do the terms “steep learning curve” and “frustration” keep coming up? Why don’t those same things come up with JS, Ruby, Go, etc? Last night I realized why that is. It’s because in Clojure you can’t get anything accomplished using only primitives.
What do I mean by primitives? I mean things that are completely transparent, you have complete control over, and don’t do anything under the covers. In C-style languages, all you need are function definitions, if statements, and for loops and you can get an awful lot done. If there’s something more complex that you need, you can create it out of those primitives.
In Clojure, you have things like map, vector, reduce, thread macros, etc that are wrappers around code that does a bunch of looping, function calls, and data manipulation for you and you have no control over how they do it. You have these black boxes that data goes in one side and different data comes out the other end. You then take these boxes and Rube Goldberg them together to accomplish tasks. If the results that come out aren’t what you want, there’s no way to open the boxes, watch the data flow through them, and change how the flow is working.
Instead of telling Clojure what you want it to do, it’s more like an IoC container where you set things up and you hope it does the right thing with it. To me, I end up feeling like a mouse hammering away at a button trying to get the cheese to come out. With other languages, you just open the box and grab the cheese.
In Clojure, until you have a utility belt with enough black boxes you understand well, there’s very little you can accomplish. That’s why it’s frustrating to learn starting out.
I can understand your frustration, but if you think that’s what Clojure is providing, you’re not “getting it” and still not thinking at the “correct” level of abstraction for Clojure to be productive for you. In a functional language, the abstractions like “sequence” and “collection” are fundamental – they essentially are the primitives, along with some “basic” operations on sequences and collections, namely “map”, “filter”, and “reduce” (and their variants) – and “composition” as a basic operation on functions themselves. Until those things are internalized as your new basis of thought, you’re going to struggle to read (and write) Clojure. Once you truly “grok” those, the issues you raise about readability will go away.
You said you understand Clojure’s concepts, but I don’t think you’ve internalized them yet as the (higher-level) primitives that the language provides: “I end up feeling like a mouse hammering away at a button trying to get the cheese to come out.”
That internalization is the “steep learning curve” and it’s particularly hard for folks who are steeped in OOP thinking, in my experience of watching people trying to learn Clojure over the last eight or nine years that I’ve been using it (in production).
Well, that was its big promise when it came along – but the reality is that it has not delivered that. The level of reusability we got with C++/Java was nowhere near what was claimed we would get. That promise was why OOP went mainstream: it was a “silver bullet” for industry to dramatically reduce the amount of code that was needed and to allow businesses to “reuse” more of that smaller amount of code. OOP has allowed for “reuse in the small” but nearly all code is tightly coupled to business domain objects and is not actually reusable across the enterprise – and it’s also partly why so many large companies that bet on Java have such monstrous, unyieldingly monolithic codebases.
I see a closure (a founding cornerstone of FP philosophy, you might say). And it’s fairly easy to build OOP-like systems in most functional languages – and that was one of the exercises in the second Clojure workshop I attended back in 2010. It’s also what Brian Marick shows in his Functional Programming for Object-Oriented Programmers book.
What we have in most OOP languages is a long way from what the originators of OOP intended – and not in a good way!
The concepts are simple. I fully grasp them. I have my sequences, maps, etc and my toolkit of stuff I can use to manipulate them. Got it. Then the rubber meets the road. I have my data, its currently at A, and I need to get it to B. How do I do that? Bam! Brick wall. I quickly find out the tools I’m familiar with are insufficient to do what I need to do. I say that confidently because every time I’ve gotten stuck and had to ask for help or look up a solution, the solution was never using things I was already familiar with. It was never a “Shoot, I should have known that!” thing. It’s always “OK, never heard of that. Good to know.” I wish I hadn’t realized it’s fundamentally IoC, though. I really, really dislike IoC. That could be part of the struggles, as well.
It actually has, but nothing is a silver bullet. The problem is they tried to use it for everything and nothing works for everything. UI programming, for example, is a great fit for it.
Yeah, can’t argue that one. I would bet money, though, that if FP took off at the same level we’d be saying the same thing about it 15 years from now.