

Crafting Code in Clojure - oskarth
http://tapestryjava.blogspot.se/2013/02/crafting-code-in-clojure.html

======
lkrubner
Clojure is different from any other language I have ever used, and as I
learned Clojure I had to struggle with the various programming habits that my
mind has acquired over the last 14 years. Here were some of the main
struggles:

1.) how do I live without a loop that lets me iterate some counters? The first
Clojure (sort of) looping structure that I understood was (reduce), so for
awhile I jumping through hoops to use (reduce) every time I wanted to go into
a loop.

2.) how do I realize lazy sequences? I got myself badly stuck with some of the
libraries, for instance Enlive, when I saved the nodes as a string and then
later re-imported them to my code, at which point they were lazy sequences and
I was wondering, how do I get this back to being Enlive nodes? Took me awhile.

3.) when do I divide code into a new file/namespace. In a language like Java
the rule is something like "one class = one file" (I could qualify that,
etc...). Clojure doesn't impose obvious dividing points on your code, and I
think it takes a while to figure out what the right dividing points are (and I
still wonder about what the best practice is, for instance: is it acceptable
for code in one namespace to rely upon, and modify, an atom in a different
namespace?)

Learning Clojure has been a fantastic educational experience for me. It is my
favorite language and my favorite development eco-system. And I am lucky
enough to be able to use at my job, so I get to work with it full time now,
for which I am very grateful. (Previously I was getting a lot of corporate
gigs where they needed PHP programmers who knew the Symfony framework -- I
found those jobs very tedious, and, more to the point, after a few years I
felt like I was not learning anything new).

~~~
mark_l_watson
Stick with it! I think that you will find Clojure + Compojure + Noir + Hiccup,
etc. will be a refreshing break from PHP.

I was mostly using Ruby with Rails or Sinatra for a long time, and the switch
to Clojure has been really good. Caveat: I have been actively using Lisp since
1982, and even so it took me a little while to switch over from Common Lisp.
Keep at it, and I think Clojure will expand the way you think about
programming.

------
julian37
Lisps are awesome! But to be fair, the Java code can be a lot more compact by
using Guava:

    
    
        import com.google.common.base.Functions;
        import com.google.common.base.Joiner;
        import com.google.common.collect.FluentIterable;
        import com.google.common.collect.Ordering;
        import com.google.common.collect.Sets;
        import java.util.Map;
        import java.util.Collection;
    
        public class MapUtils {
            public static String sortedKeyList(Map<?, ?> map1, Map<?, ?> map2) {
                Collection<String> sortedKeys = FluentIterable
                        .from(Sets.union(map1.keySet(), map2.keySet()))
                        .transform(Functions.toStringFunction())
                        .toSortedSet(Ordering.natural());
    
                return sortedKeys.isEmpty()
                        ? "<none>"
                        : Joiner.on(", ").join(sortedKeys);
            }
        }
    

Not nearly as pretty as the Clojure code, but a lot nicer than without Guava.

~~~
doktrin
This is a good point.

I personally find the "code compactness" to be a bit of a red herring. Java's
greatest weakness, after all, is not the verbosity per se. If all of its other
failings were addressed I would probably scarcely care about the amount of
code it takes to perform a given task.

Likewise, compactness is not what lies behind the current FP trend.
Compactness, after all, while related to programming paradigms is more
directly tied to the strength and level of abstraction afforded by a given
library / API. Ruby's standard library, for instance, allows for some pretty
insane one liners.

This is to say nothing of the development time overhead involved in switching
paradigms (non-trivial, IMHO). This alone offsets "compactness", both from the
standpoint of up front dev time as well as readability / correctness, for
(IMO) up to half a year.

As an aside, that code could be made yet more compact if written in Groovy
(Groovy-as-a-Java-superset and not functional Groovy, necessarily).

~~~
zmmmmm
> that code could be made yet more compact if written in Groovy

Indeed. It's very hard to read this and not think ... "but this would just be"
in Groovy:

    
    
        [m1,m2]*.keySet().sum().sort().join(",")?:"<none>"
    

So small that I would rarely even make a separate function for it. I imagine
it's much the same in many dynamic languages.

~~~
enigmo
And in many static languages...

------
pjscott
Because this is turning into a fun language code-off, here's something in
Python:

    
    
        ', '.join(sorted(str(x) for x in set(map1) | set(map2))) or '<none>'
    

This works by making sets of the keys of map1 and map2, taking the union of
those sets, converting each key to a string, sorting those strings, and
joining them with ', '. And then if that generated an empty string -- if both
dicts were empty -- then it evaluates to '<none>'.

Am I missing some difficulty here?

EDIT: and for an arbitrary number of maps, this one works:

    
    
        import itertools
        ', '.join(sorted({str(x) for x in itertools.chain(*maps)})) or '<none>'
    

It iterates over all the keys of all the maps with itertools.chain(), converts
everything to a string, adds everything to a set, sorts them, and then joins
them with commas. I realize it's not a one-liner anymore because of the
itertools import, but it's still pretty simple to follow.

I like Clojure, but for problems like this, it just doesn't seem to be doing
quite as well as languages like Python.

~~~
jshen
I've never liked python code like this because I can't read it in left-to-
right order nor right-to-left order. You have to read into the comprehension,
then read left, then finally jump far right to the 'or'

------
dizzystar
Nice article! I have one question for others though: Am I the only one who
disagrees with ->> being easier to read?

From the OP:

    
    
      (->>
              (set (concat (keys map1) (keys map2)))
              (map str)
              sort)
    

And also from the OP:

    
    
      (sort (map str (set (concat (keys map1) (keys map2)))))
    

I'm so used to reading Lisp "in to out" that I have a difficult using the
thread-fast macro. I guess it depends on your background. Do those who have
have a bit of Lisp experience agree with me?

~~~
lispm
I'm used to read Lisp indented:

    
    
        (sort (map str
                   (set (concat (keys map1)
                                (keys map2)))))

------
dxbydt

         def f[T](a:Map[String,T],b:Map[String,T]) = {
           val res= (a.keys++b.keys).toList.sorted.mkString(",")
           if (res.length == 0) None else Some(res)
         }
    

<https://gist.github.com/4694911>

~~~
aaroniba
The equivalent clojure code would be:

    
    
        (defn f [a b]
          (let [res (join "," (sort (distinct (mapcat keys [a b]))))]
            (if (empty? res) "<none>" res)))
    

Also I'm not a Scala expert, but I think you want .keySet instead of .keys,
otherwise they won't be unique, right?

~~~
dxbydt
> you want .keySet instead of .keys, otherwise they won't be unique, right?
    
    
         scala> val p = Map(1->4,5->3)
         scala> p.keys
         res0: Iterable[Int] = Set(1, 5)
         scala> p.keySet
         res1: scala.collection.immutable.Set[Int] = Set(1, 5)
         scala> p.keys++p.keys
         res2: Iterable[Int] = Set(1, 5)
         scala> p.keySet++p.keySet
         res3: scala.collection.immutable.Set[Int] = Set(1, 5)

------
lispm
Common Lisp

    
    
        (defun example (&rest maps)
          (format nil "~:[<none>~;~:*~{~A~^, ~}~]"
                  (sort (remove-duplicates
                         (loop for map in maps nconc
                               (loop for key being the hash-key of map collect key))
                         :test 'equal)
                        'string<)))

------
tel
Golfing it for Haskell

    
    
        go :: (Show k, Eq k) => Map k v -> Map k v -> String
        go m1 m2 = map show >>> sort >>> intersperse ", " >>> mconcat >>> orNone
                   $ keys m1 ++ keys m2
          where orNone [] = "<none>"
                orNone e  = e

~~~
evincarofautumn
Some quick tips. “intersperse ", " >>> mconcat” is “intercalate ", "”, and I
would prefer “.” to “>>>”. Even though left-to-right composition reads better
to me, right-to-left is the style.

Gratuitious pointfree version (imports omitted):

    
    
        go :: (Show k, Eq k) => Map k v -> Map k v -> String
        go = orNone . intercalate ", " . sort . map show .: (++) `on` keys
          where
          orNone [] = "<none>"
          orNone e = e
          f .: g = (f .) . g
          infixr 8 .:

~~~
tel
Nice! I had (.) originally but reorganized it to compare better with the
Clojure code. I really like (.:) and on though (which I'm always forgetting
about).

------
bfe
It's great how he walks through the mental process of iterating his code, with
examples at each step, and how it demonstrates the power of the language.

------
shoo
Since someone had to do it, here's a version in brainfuck:

<https://github.com/fcostin/crafting_code_in_bf>

Writing things in brainfuck is a good way to appreciate the things we
sometimes take for granted, e.g. languages with more than 1 pointer.

------
markov_twain
so this is horribly non-idiomatic ruby, but it was fun

    
    
      p ->(*_) {
        _.reduce({}, :merge).tap { |_| return "<none>" if _.empty? }.keys.uniq.map(&:to_s).sort.join(", ")
      }.({c:3,d:4,b:2}, {f:5,e:4,a:1})
      
      p ->(*_) {
        _.reduce({}, :merge).tap { |_| return "<none>" if _.empty? }.keys.uniq.map(&:to_s).sort.join(", ")
      }.({}, {})
      
      # "a, b, c, d, e, f"
      # "<none>"
    

edit: a little bit better.

    
    
      func = ->(*_) {
        return "<none>" if _.all?(&:empty?)
        _.flat_map(&:keys).uniq.map(&:to_s).sort.join(", ")
      }
      
      func.({c:3,d:4,b:2}, {f:5,e:4,a:1})
      #=> "a, b, c, d, e, f"
      func.({}, {})
      #=> "<none>"
    

<https://gist.github.com/66b45e765a2aa6a97143>

~~~
griffindy
slightly more idiomatic, though perhaps not quite as clear as the clojure:

    
    
        def unique_keys(*hashes)
          result = {}
          hashes.each do |hash|
            result.merge! hash
          end
          return result.keys.map(&:to_s).sort.join(', ') unless result.empty?
          '<none>'
        end
    

<https://gist.github.com/4695388>

~~~
danneu
I think accumulating an initialized variable in an imperative #each loop is
decidedly unidiomatic, just something we tend to do until we really embrace
things like #map, #partition, #select, #reduce, etc. (And what a glorious day
that is!)

I reckon what makes his first example somewhat unidiomatic is the arbitrary
lambda and the use & inner return from the #taps block.

His updated example is just good ol tacit Ruby. But then again the amount of
times I see an #each block with inner logic used instead of a simple point-
free one-liner in Ruby really just proves your point.

------
robertfw
Thanks for this post! I'm diving into clojure (currently a python dev), and
I'm starting to get that tingly anticipation of being able to use these kinds
of tools. It was really useful to see how you iterate through your solution.

~~~
eckzow
That's funny--as a python guy I was squirming for a set comprehension
immediately:

    
    
      >>> a = {'a':1,'b':2}
      >>> b = {'b':3,'c':4}
      >>> def keylist(*maps):
      ...     return ', '.join(sorted({str(k) for m in maps for k in m.keys()})) or '<none>'
      ...
      >>> keylist(a, b)
      'a, b, c'
      >>> keylist()
      '<none>'
    

I'm sure people will have differing opinions on the readability of a nested
set comprehension, but from the bullets at the top of the article to one-liner
implementation in ~30 seconds. (For an arbitrary number of maps, no less.)

(edit: added '<none>' on empty, fixed formatting, whoops now I'm over 30
seconds :))

~~~
robertfw
Someone pointed out elsewhere in the thread that this kind of code has to be
read from the inside out, and for that reason I think the clojure code makes a
better balance of readable vs succinct.

------
halter73
Since most other popular languages seem represented:

    
    
            string SortedKeyList<T, U>(params IDictionary<T, U>[] maps)
            {
                var keys = new SortedSet<string>(maps.SelectMany(m => m.Keys)
                                                     .Select(k => k.ToString()));
                return keys.Any() ? String.Join(", ", keys) : "<none>";
            }
    

EDIT: I just realized that a lot of curly brace languages look the same to the
uninitiated. The above is C#.

------
petercooper
A Ruby version that attempts to be as close to the elegant and readable
Clojure solution as possible via similar 'crafting':

    
    
        def sorted_key_list(maps)
          maps.map(&:keys)
              .reduce(:&)
              .sort
              .tap { |a| a.push "<none>" if a.empty? }
              .join ","
        end
    

Works out at 24 syntax-related punctuation symbols for Ruby, 20 for Clojure
(ignoring the replace-empty function, admittedly).

~~~
awj
You actually want ".reduce(:|)", & does intersection on arrays. You can
replace that .tap block with gsub abuse for a bit more golfing, and can
replace the map/reduce combo with flat_map/uniq to eliminate one symbol-to-
proc:

    
    
        def sorted_key_list(maps)
          maps.flat_map(&:keys)
          .uniq
          .sort
          .join(",").gsub /^$/, "<none>"
        end

~~~
petercooper
D'oh, I misread the spec; this is where understanding the Clojure properly may
have helped too. I thought the keys had to be _in_ both maps, not the keys
_from_ both maps. Good call on the | and flat_map then :-)

