Hacker News new | comments | show | ask | jobs | submit login

>>But languages have changed and Java (1.4 -> Java 8 and beyond), Python (2.1 -> 3.7) and Perl (5.7 -> 5.28.0) have all matured quite a bit since then. And then there's the ideas of functional programming that are seeping into other languages.

Apart from Perl no other language has lispy features like CL. Clojure is the only language that comes close. Perl is the only option you have even now if you want to do decent enough higher order practical programming.


Also there is big difference between having some token features(like lambdas) bolted on as a after thought and enabling fully blown functional programming which are well designed and integrated into the language.

Apart from Perl, Functional programming looks very half baked, hard and brittle in most of these languages.

If you want a full featured programming language, including both Functional and OO styles, take a look at Perl 6. Its designed to work will all programming styles.

>>This article aged worse than the bumperstickers from '85 that's also currently on the front page.

Every single thing from that article is true. What are you talking about?

> Java has the Vector type, which allows for sequences of dynamically varying length

The Vector type in Java has been not best practice since Java 1.5 when the Collections framework came out. In particular, the Java 1.4 days and before all of the collection classes were synchronized leading to some not insignificant performance hits (even in single threaded applications). ArrayList and LinkedList are the classes that came with the rework. In particular, while Vector implements List, its backed by an array and so various list like operations (like adding a large number of elements without presizing the array) can take a performance hit.

> Automatic Storage Management. Java and Python support this. Lisp implementations tend to be more mature and perform better.

There have been numerous changes to the JVM for garbage collection since 1.4. While I can't speak to LISP's garbage collection implementations, Java has come a long ways in the past 16 years.

> Dynamic Typing. Java attaches run-time type information to instances of the class Object, but not to primitive data elements. However, Java requires a type declaration for every variable. This has some advantages for production code, but has disadvantages for rapid prototyping and evolution of programs. Java does not have a generic/template system that would allow types life Vector<String>, and suffers greatly because of it.

Specifically, Vector<String> has been available since Java 1.5. Java 10 brings local type inference ( https://developer.oracle.com/java/jdk-10-local-variable-type... ) so instead of

    Map<User, List<String>> userChannel = new HashMap<User, List<String>>();
in Java 1.7 one can write:

    Map<User, List<String>> userChannel = new HashMap<>();
and in Java 10:

    var userChannels = new HashMap<User, List<String>>();
> First-Class Functions. Java has anonymous classes, which serve some of the purposes of closures, although in a less versatile way with a more clumsy syntax. In Lisp, we can say (lambda (x) (f (g x))) where in Java we would have to say new UnaryFunction() { public Object execute(Object x) { return (Cast x).g().f(); } }

Lambdas are available in Java 8. https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaex...

As a note, the bit about python closures being read only ("The only drawback is that closed-over variables are read-only.") gets to a very interesting debate on first class environments that is described at http://funcall.blogspot.com/2009/09/first-class-environments...

> Interactive Environment. Some Java environments allow Lisp-like features such as an intgeractive command loop and stop-and-fix debugging. Lisp environments are still ahead, but that probably won't last long. BlueJ in particular has most of the major features of a good Lisp environment: you can recompile a method into a running program, and you can type in an expression and have it evaluated immediately. It is intended for teaching purposes, and I can't tell if it is suitable for production use. Python has the same interactive approach as Lisp, but the environment is less mature than Lisp's.

Eclipse, Netbeans and IntelliJ all have rather advanced debuggers. Eclipse has a 'display' view as shown in https://www.ibm.com/developerworks/library/os-ecbug/ that allows compilation and execution of arbitrary Java code.

> Extensibility. This may prove to be Java's weak point. Java works well as long as you are willing to make everything a class. If something new like, say, aspect-oriented programming takes off, Lisp would be able to incorporate it with macros, but Java would not.

Aspect oriented coding is well supported in Java with libraries such as https://en.wikipedia.org/wiki/Spring_Framework#Aspect-orient... and https://en.wikipedia.org/wiki/AspectJ .


Yes, in the Java 1.4 world, what was said was true. That is a very, very long time ago for Java and many of the criticisms that were provided are no longer valid.

The higher order functions of Java are not as Lispy as Perl's, but they exist as Streams. In Java 8, one can write:

    int sum = widgets.stream()
        .filter(b -> b.getColor() == RED)
        .mapToInt(b -> b.getWeight())
Note that filter and mapToInt are both taking lambdas are arguments.

I would encourage you to glance at Groovy which in Java is my favorite Perl.

    ['a', 'b', 'c'].eachWithIndex { it, i ->
        println "$i: $it"
or for a perl style map:

    assert [1, 2, 3].collect { it * 2 } == [2, 4, 6]
and a perl style grep:

    assert [1, 2, 3].findAll { it > 1 } == [2, 3]
It also has nice support of closures, lambdas currying and other functional programing concepts ( http://groovy-lang.org/closures.html ). There's also some neat things that one can do with JSR 223 and groovy - http://groovy-lang.org/integrating.html#jsr223

Sorry, none of this comes remotely close to what Lisp provides.

Also the code you wrote won't pass production code review at any decent Java shop.

If you want you can do very functional stuff in Groovy. Here's an example of function composition:

   f = { it * 3 }
   g = { it + 4 }
   h = f << g
The reason this is not particularly common usage is not because you can't do it, but because most people don't find it very intuitive.

The unintuitive part is the choice of operator `<<` instead of `>>`. in your example `f << g` means do `g` first (i.e. 2 + 4 = 6) then do `f` on the result (i.e. 6 * 3 = 18). I would have expected `f >> g` for this behavior.

I really don't follow your expectation here. "Data moves in the direction of the arrows" seems natural to me. Can you unpack it at all? I'd like to understand where you're coming from.

It's following the semantics of wider use of `<<`` in groovy which denotes forwarding contents of one object into another, eg: adding to a list

    list << x
or writing to a stream:

    new File('test.txt') << 'hello world'
So if you read it as "forward the result of g into f" then it makes a little more sense.

But I agree, it's still not very intuitive and for readability I would normally just write:

    h = { f(g(it)) }
Which I think is far clearer. But that's partly an argument against functional programming style itself which would ordinarily see function composition as more idiomatic.

None of those Apache Groovy examples you gave work when scripting Jenkins pipelines, because the all the functional methods (e.g. each, collect, findAll) are crippled so they don't work. Groovy is more like Bash for Java, rather than Perl.

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact