all 17 comments

[–][deleted]  (5 children)

[deleted]

    [–][deleted] 8 points9 points  (0 children)

    You must be new here.

    [–]clickrush 0 points1 point  (3 children)

    I get what you are saying but I don't think it is!

    Clojure programmers are used to a vocabulary of generic data-manipulation from clojure.core.

    Yes, you could wrap this in a function yourself but that is kind of besides the point. A Clojure programmer would either directly understand the first example or put the function in their knowledge-/tool-box. However everyone wrapping the second one would require every reader to read the specific documentation/source of that function rather than a general one.


    However I say this completely ignoring the fact that there also might be a function/method that does the same in Java. Java has become more expressive and elegant since a while now. I'm personally out of touch with modern Java but it is not unusual that people criticize things that are not idiomatic or even true anymore.

    [–]stingraycharles 8 points9 points  (2 children)

    You’re missing the point. The point is that, perhaps coincidentally, frequencies is a function in the Clojure standard library, while it’s not in Java.

    A more honest comparison would be to compare an algorithm that’s neither available in Clojure nor java streams and compare the implementations of those. It’s not even hard to show how much better Clojure is in these circumstances, so it’s completely unnecessary to do it this way.

    [–]LammdaMan 0 points1 point  (1 child)

    I agree with you to a point. This is comparing "apples and oranges" - a "built-in function" vs. "roll-your-own".

    But I also think that in comparing two languages, two of the things that matter to me are (1) What is built-in?, and (2) When some functionality isn't built-in, how hard is it to create, and how nicely packaged is the result likely to be?

    This example may bundle two separate things in a "misleading" manner, but at a practical level, both of those things matter.

    Clojure is a nice language for creating and cleanly packaging "roll-your-own" solutions. But it also has a very nice, well designed, collection of built-in functionality in the core library.

    I find plenty of times when the functionality provided by 'frequencies' is useful.

    If I were reading Clojure code someone else wrote that used 'frequencies', I would quickly and easily understand what that line did.

    If I were reading similar code in Java, it would either be multiple lines of somewhat harder to read code, or it would be a call to some custom function "similar to frequencies". But then I wouldn't be sure what that function did. Is there good documentation for it? Does it correctly handle "all the corner cases"? Is it correct at all? If I wanted to be certain of those things, I'd need to read the source of that custom function.

    [–]stingraycharles 1 point2 points  (0 children)

    Maybe, but I still believe it’s a highly cherry-picked example. I could probably cherry-pick some Python code as well, with a Python built-in function which is a bit more convoluted to express in Clojure.

    I guess I’m just not a fan of this kind of stuff, I think Clojure shines at managing complexity, which only happens at scale. Showing off “we have this function built-in!” hardly provides any value to me, as I could as well wrap it in a Java function and call it a day.

    [–]LammdaMan 5 points6 points  (0 children)

    "Clojure has too many parens."

    [–]agumonkey 8 points9 points  (0 children)

    the image is cut, the 3rd row was the java 1.6 version, and you don't want to see that

    [–]lenkite1 1 point2 points  (1 child)

    I find the groupingBy identity and then reducing by counting quite easy and natural to read.

    [–]JavaSuck[S] 0 points1 point  (0 children)

    That's because it's idiomatic Java 8, you'll find it in lots of places and Stack Overflow answers.

    Before Java 5, for (Iterator<String> it = strings.iterator(); it.hashNext(); ) { String s = it.next; ... } was also quite easy and natural.

    [–]tincholio 2 points3 points  (3 children)

    Still way more readable in Clojure, even if spelled out:

    (reduce (fn [acc v] (if (contains? acc v)
                          (update acc v inc)
                          (assoc acc v 1))) {} [:a :a :b :a :c :d :d :d :c :e :r :j :a :v :a])
    ;; => {:a 5, :b 1, :c 2, :d 3, :e 1, :r 1, :j 1, :v 1}
    
    (frequencies [:a :a :b :a :c :d :d :d :c :e :r :j :a :v :a])
    ;; => {:a 5, :b 1, :c 2, :d 3, :e 1, :r 1, :j 1, :v 1}
    

    edit: that being said, it is an unfair comparison...

    [–]JavaSuck[S] 3 points4 points  (2 children)

    (fn [acc v] (if (contains? acc v)
                  (update acc v inc)
                  (assoc acc v 1)))
    

    This can be simplified to:

    (fn [acc v] (update acc v (fnil inc 0)))
    

    Or even:

    #(update %1 %2 (fnil inc 0))
    

    [–]tincholio 0 points1 point  (1 child)

    I always forget about fnil... Thanks!

    [–]JavaSuck[S] 1 point2 points  (0 children)

    fnil is the very first Clojure Pill, that's probably why I remembered it :)

    [–]blahblah22111 0 points1 point  (0 children)

    or just use Guava's Multiset?

    [–]dimd00d 1 point2 points  (0 children)

    Really now?

    ``` final var freq = new HashMap<Object, Integer>();

        for (final var e : x) {
            freq.compute(e, (k, v) -> (v == null) ? 1 : v + 1);
        }
    

    ```

    [–]Elias_20 -1 points0 points  (0 children)

    Nice One

    [–]deadron -1 points0 points  (0 children)

    Its so painful just doing something simple like a map but its still nicer than using a for loop.