And on readability, what do you prefer?
List<Integer> even = new List()
for(int i = 0; i < 100; i++){
if((i%2)==0){
even.add(i);
}
}
return even;
vs. (filter even? (range 0 100))And on readability, what do you prefer?
List<Integer> even = new List()
for(int i = 0; i < 100; i++){
if((i%2)==0){
even.add(i);
}
}
return even;
vs. (filter even? (range 0 100))IntStream.range(0, 100).filter(i -> i % 2 == 0).collect(Collectors.toList());
Disclaimer: I'm not writing Java nowadays, so this is my quick prototype from a quick google-session, it probably won't compile :D
Edit: looks like I'm second, my google-session took too long :-)
Maybe bonus points for C#: Enumerable.Range(0, 100).Where(i => i % 2 == 0);
This has been added to java to catchup with functional programming languages.
In the Java version, I need to know/learn IntStream.range, IntStream.filter, IntStream.collect, the even lambda, and Collectors.toList
Of course, this is comparing one-liners, so doesn't necessarily translate to larger programs. Just pointing out that I don't find this one particular example to be in Javas favour.
I am sure functional programming has its natural appealing in areas like recursion, but this example doesn't seem too convincing for winning readability, I can always write this in python
filter(even, range(0, 101)))
where even is a function I wrote.
Please give an example for "Clojure allows me to express thoughts that I wouldn't know how to express in for, while, if, variables and objects." if you can.
But sure, let's make things a bit harder. As below, let's produce a sequence of the form [true false false true true true false false false false ...] of exact length 1000 without computing any more than exactly 1000 elements.
(take 1000 (mapcat (fn [i] (repeat i (odd? i))) (range)))
Of course the really interesting stuff is hard to do in one liners. But I think one of the more mind bending things is probably core.async. Go style coroutines and channels implemented as a library on top of the language. Which allows for concurrent programming inside clojurescript which is compiled to JS and thus inherently single-threaded.Readability and language understanding are not separable. I for one have a hard time actually understand what the code does above (although I can guess it). But I am not sure how ^ translate into [true false false true true true false false false false ...] sequence because, I thought we were calculating oddity of 1000 numbers.
[1 2 3 4 5 6 7 8 9 10 ...] ==> [T F T T F T F T T T ]
Perhaps the lack of understanding Clojure syntax to comprehend what the code is doing.
Python also allows for functional programming to a certain degree, but clojure is a lot more powerful when it comes to language extensions.
Threads as a library (for js environments) for clojure. https://github.com/clojure/core.async
Logic programming for clojure. https://github.com/clojure/core.logic
Declarative UI building, with dataflow semantics. http://reagent-project.github.io
Additionally immutability makes your life a lot easier. Parallel computing in clojure is a no brainer, you just change your map to pmap, and that's it.
I have always wanted to look at building something again in one of the popular FPLs.
In Clojure, readability is obtained through standardization. Every Clojure programmer instantly knows what the functions in the standard library do. Because there's not much syntax in a Lisp, code is very easy to read once one knows the functions.
"filter" is incredibly useful, and most modern languages (including Java 8) have it built in.
"even?" is sometimes useful, and languages are kind of hit-and-miss about it, but its usually small enough to inline, and the biggest cost of not having it built-in is probably things like people using modulus rather than first-bit testing in Java which, IIRC, produces somewhat slower code for that check.
So, again, python:
[i % 2 == 0 for i in range(1000)]
(If you insist on Java, its fairly straightforward, though not a one-liner AFAIK, using Java 8 generators.)To compare it with what your code produces.
[true false false true true true false false false false ...]
[true false true false true false true false true false ...]
Edited for more clarity.
from itertools import chain, repeat
chain.from_iterable([repeat(i % 2 != 0, i) for i in range(1,1000)])
In Java 8, I think the solution with streams would be fairly straightforward, something like: IntStream.range(1,1001).flatMap(i -> Stream<Boolean>.generate( () -> (x & 1) != 0 ).limit(i))So, I think I need to take a break from comment-box programming for a while; its clearly not working out for me today.
And this example is suppoed to support Clojure's readability?
You'd btw probably write it as.
(->> (range)
(map #(repeat % (odd? %)))
(apply concat)
(take 1000)
If you want it more readable.Not an FP expert so I don't know if this is entirely accurate or how it plays out in reality.
Any competent Clojure programmer would likely have known most of the Clojure core functions by heart: map, reduce, filter, etc. There are maybe about a hundred of them?
Unlike imperative programming languages, where you use three primitives (assignment, condition and loop) to construct everything, in Clojure, you use these core functions to construct your program. In a sense, the process of learning Clojure is the process of learning the use of core functions. These functions are simply at a higher level of abstraction than the three primitives, hence some people say that Clojure is more expressive, because it's closer to human thoughts than Von Neumann machine specificity.
I really like Clojure, but I've always found the relatively huge set of core functions to be a major barrier to entry. I always have this nagging feeling of "there's probably a core function I'm missing to make doing this or that easier", and end up spending a lot of time poring over documentation instead of just programming. Maybe that's just how it is, and the answer is to push through it until you know the functions better, but there's something to be said for shipping a language with fewer, simpler, more primitive operations.
Human has unlimited memory capacity and fast memory access, whereas human logical processing power is fairly limited and error prone. That's why any real human language has a large vocabulary.
You may find https://github.com/jonase/kibit helpful for some things. Otherwise I'd recommend just dedicating time to exploring the builtins so that you can resist the urge to wander while you should be programming -- there was a very interesting series a few years back some blogger made that covered all of Python's builtin modules, doing it yourself (or maybe there's one now for Clojure) is worthwhile. I also like to visit things like http://www.clojure-toolbox.com/ from time to time to see if there's anything new and exciting in areas of my interest.
Would you prefer if there wasn't such a function to make it easier? You lose nothing if there isn't (or you don't know about it) and gain if there is. Why is this a barrier to entry?
[0]: https://clojure.github.io/clojure/clojure.core-api.html
Therefore, in your question, you could substitute Clojure with functional programming, and the examples in which FP allows you to express thoughts you wouldn't know how to express in for, while and so on -- are plentiful.
I may not be answering your question, but there's a reason for that: the functional programming aspect isn't Clojure's main selling point. It's the other ideas. Software transactional memory, immutability, its emphasis on state and identity, the protocol system, transients, concurrency, transducers, the list goes on.
I am definitely not dismissing FPL, I am just arguging about the actual examples given were not strong enough to dismiss Java being hard to read.
Lambda calculus (the theoretical foundation for all functional languages) was initially untyped.
The main benefit of modern functional languages is in their use of revisioned data structures. This has same benefits as garbage collection.
Without GC you have to manually track where you allocate and deallocate things, and this is highly error prone. With GC the runtime does this for you, and you can focus on other things.
Same thing applies with mutable and immutable data. With mutable data structures all the changes are done in place, and you have to keep track of everywhere it's used to update it predictably. With immutable data structures you're "copying" the data any time you make a change, and the runtime keeps track of the references for you.
No one wants to admit that their language choice is because of the cultural norms in their language community, but if you can hide some complexity behind a language construct, you can hide the same complexity behind a function call. It's just a question of how much of the work required to do that has already been done by the community.
You're just not going to win the "my language can do x more concisely than a function called x in your language!" argument. You're better off with "this is easy and my language and you'd have to write a function to do it in yours."
The problem is the language culture and what is idiomatic. I write Python in my day job and while I could certainly write immutable data structures in Python and code in a way that treats data as immutable, it would be going against what is idiomatic in the language, which means that it won't play well with libraries and that my coworkers can still trample over my data without my knowledge. That is, the language gives me no guarantees whatsoever that my immutability is actually respected and that other code somewhere else that I did not write (either application code written by a coworker or library code written by a stranger) respects the immutability.
I find this argument similar to asking why bother writing in any high level language when you can implement the same things in assmembly. Hell, why don't we all just program turing machines directly?
Languages carry a lot more with them than just the features - they carry idioms and culture and community trends. All of these (and even small syntax differences) influence how we think about problems and how we create and structure solutions. They also influence how my code will interact with other peoples (and theirs with mine).
Although, I don't think the functional/ imperative is necessary a good axis for categorizing languages. There are potentially more differences between Clojure/Ocaml than Python/Clojure, for example.
user=> (source even?)
(defn even?
"Returns true if n is even, throws an exception if n is not an integer"
{:added "1.0"
:static true}
[n] (if (integer? n)
(zero? (bit-and (clojure.lang.RT/uncheckedLongCast n) 1))
(throw (IllegalArgumentException. (str "Argument must be an integer: " n)))))Besides, Clojure doesn't even win in the one line department against modern JVM languages (e.g. Kotlin).
Surely that has nothing to do with Java being 20 years old, and Clojure 1.0 being 6 years old?
> Clojure doesn't even win in the one line department against modern JVM languages (e.g. Kotlin).
Example, please.
Lisp has been around for 50+ years, hasn't really helped its popularity much. But I'm willing to bet with you that in 14 years from now, Clojure will have even fewer users than it has today.
> > Clojure doesn't even win in the one line department against modern JVM languages (e.g. Kotlin).
> Example, please.
Here it is in Kotlin:
(0 .. 20).filter { it % 2 == 0 }Computer science has too short a history to make any sound arguments from history. However, if you insists on arguing from history, let's look at the long term trend. Would you agree that the trend of programming language is progressing to higher and higher level of abstraction and further and further away from the machine details? From machine code, to assembly language, to C, to Java, and now FP languages, this trend of mainstream programming language evolution is very clear. In this sense, if you take Lisp as a pioneer of function programming, it was simply ahead of its time.
If you are implying that Lisp syntax is somehow an impediment to its popularity. That may well be. It's a personal preference issue. Some people like more syntax in their language, some people like less. If you are in the later camp, Lisp is surely very attractive. For example, in your kotlin one-liner, there are just too much syntax to understand "()", "..", ".", "{}", etc. Life is too short, I just don't want to learn all these idiosyncrasy invented by someone just for the sake of being different. I wished I had knew Lisp sooner, certainly not having programmed for 20 years.
I am sure you will lose that bet.
That's...less than clear. The extreme high level of abstraction in PLs is consistently increasing, or at least non-decreasing, over time, but the mean/median used in actual programming may not be.
Basically, you are saying, when the max of a sequence is increasing, the mean/median stays or even decreases. That would require that the frequencies on the lower end to increase much faster than that on the higher end, which is certainly not the case. For example, the number of C programmers is growing faster than the number of Java programmer today? No one would think that's correct.
Even if CS is still pretty recent (50+ years), there is still something to be said about a language that never managed to become mainstream in that period. Whether Lisp was ahead of its time is irrelevant. The question is: will there ever be a time where Lisp will be ripe for its time?
I think that window closed a while ago. It's not about the parentheses or its FP side, it's just that the world is moving toward statically typed languages with sophisticated type inference, and it's a league that Lisp simply doesn't belong to.
The billion lines of Javascript, Python, Ruby and R would like to differ.
Each of the first three has probably more users than all languages (ML family, haskell, scala) with an advanced type system combined.
I love dependent typing, I truly do. But the right tool for the job, and a formally verified coq program is certainly suited for a rocket, but not for the high iteration and velocity environments most people program in.
https://www.google.com/trends/explore#q=java&cmpt=q&tz=Etc%2...
And Clojure, not many is interested in it:
https://www.google.com/trends/explore#q=clojure&cmpt=q&tz=Et...
Actually I'm on the Clojure side. News comer will always pick a language which is simpler, more expressive, newer. Java is no longer such a language comparing to others.