9

I am new to clojure and the main thing I am struggling with is writing readable code. I often end up with functions like the one below.

(fn rep 
  ([lst n]
    (rep (rest lst)
    n
    (take n 
      (repeat (first lst)))))
  ([lst n out]
    (if
      (empty? lst)
      out
      (rep 
        (rest lst) n
        (concat out (take n 
          (repeat 
            (first lst))))))))

with lots of build ups of end brackets. What are the best ways of reducing this or formatting it in a way that makes it easier to spot missing brackets?

Jim Jeffries
  • 9,841
  • 15
  • 62
  • 103
  • 1
    That's `Lisp-anese` for you. Using a decent editor that helps with bracket matching is one route to take. – Jason Down Apr 10 '12 at 19:39
  • 2
    You could always put single parenthesis on their own lines and line them up with the opening parenthesis (like the standard curly brace syntax for C# or Java). Some hardcore Lispers may utter indecencies at you however :) – Jason Down Apr 10 '12 at 19:44
  • 7
    I guess I'll step in as the "hardcore lisper", even though I've only been using lisp for two years. Putting parens on their own line is like the cardinal sin of formatting - there is no worse choice you could make. The many dialects of lisp have all (more or less) settled on a single coding style because it's fairly readable, unlike the C-like languages, which all have many styles that inspire holy wars. Don't abandon the standard style just because you haven't gotten the hang of it. – amalloy Apr 10 '12 at 20:40

8 Answers8

15

Using Emacs's paredit mode (emulated in a few other editors too) means you're generally - unless you're copy/pasting with mouse/forced-unstructured selections - dealing with matched brackets/braces/parentheses and related indenting with no counting needed.

Emacs with https://github.com/technomancy/emacs-starter-kit (highly recommended!) has paredit enabled for clojure by default. Otherwise, see http://emacswiki.org/emacs/ParEdit

Joost Diepenmaat
  • 17,633
  • 3
  • 44
  • 53
  • 1
    I HATED paredit when I first started using emacs. But once I understood what it was doing for me, (http://www.emacswiki.org/emacs/PareditCheatsheet) I not can't live without it. – Matthew Boston Apr 11 '12 at 18:35
  • I selected the other answer as I am a vim user, but +1 for paredit – Jim Jeffries Apr 12 '12 at 12:51
13

In addition to having an editor that supports brace matching, you can also try to make your code less nested. I believe that your function could be rewritten as:

(defn rep [coll n] (mapcat (partial repeat n) coll))

Of course this is more of an art (craft) than science, but some pointers (in random order):

  • Problems on 4clojure and their solutions by top users (visible after solving particular problems) - I believe that Chris Houser is there under the handle chouser
  • Speaking of CH - "The Joy of Clojure" is a very useful read
  • Browsing docs on clojure.core - there are a lot of useful functions there
  • -> and ->> threading macros are very useful for flattening nested code
  • stackoverflow - some of the brightest and most helpful people in the world answer questions there ;-)
amalloy
  • 89,153
  • 8
  • 140
  • 205
Rafał Dowgird
  • 43,216
  • 11
  • 77
  • 90
  • Thanks for the tips! I've been solving each 4clojure problem a couple of times where possible, at least once with recursion and at least once with out. This was just an example with a lot of brackets. Yours tips above will no doubt be very useful though – Jim Jeffries Apr 11 '12 at 12:42
7

An editor that colors the parenthesis is extremely helpful in this case. For example, here's what your code looks in my vim editor (using vimclojure):

Rainbow colors

Since you didn't say which editor you use, you'll have to find the rainbow-coloring feature for your editor appropriately.

Jeff
  • 5,013
  • 1
  • 33
  • 30
  • for emacs see this SO question: http://stackoverflow.com/questions/2413047/how-do-i-get-rainbow-parentheses-in-emacs – Arthur Ulfeldt Apr 10 '12 at 20:40
  • I used rainbow parens in emacs for a while. I don't anymore, largely because I think that encourages you to actually do manual/mental paren matching, instead of letting paredit do it all for you. But I confess that rainbow parens *looks* pretty cool, so if you're doing it just for looks I can't really argue. – amalloy Apr 10 '12 at 22:03
  • @amalloy: can you use *paredit* and *rainbow-parens* at the same time? – TacticalCoder Apr 11 '12 at 14:35
  • @TacticalCoder Yes. I'm doing so right now. I agree it looks pretty, and it doesn't get in my way, but I don't think I really get much out of it, productivity-wise. (What I, personally, really like is paren matching with full-expression highlighting, which Emacs can also do.) – Matthias Benkard Apr 11 '12 at 19:03
3
(fn rep 
  ([lst n]
    (rep lst n nil))
  ([lst n acc]
    (if-let [s (seq lst)]
      (recur (rest s) n (concat acc (repeat n (first s))))
      acc)))

that's more readable, i think. note that:

  • you should use recur when tail recursing
  • you should test with seq - see http://clojure.org/lazy
  • repeat can take a count
  • concat will drop nil, which saves repeating yourself
  • you don't need to start a new line for every open paren

as for the parens - your editor/ide should take care of that. i am typing blind here, so forgive me if it's wrong...

[Rafał Dowgird's code is shorter; i am learning too...]

[updated:] after re-reading the "lazy" link, i think i have been handling lazy sequences incorrectly,

andrew cooke
  • 45,717
  • 10
  • 93
  • 143
3

I cannot echo strongly enough how valuable it is to use paredit, or some similar feature in another editor. It frees you from caring at all about parens - they always match themselves up perfectly, and tedious, error-prone editing tasks like "change (foo (bar x) y) into (foo (bar x y))" become a single keystroke. For a week or so paredit will frustrate you beyond belief as it prevents you from doing things manually, but once you learn the automatic ways of handling parens, you will never be able to go back.

I recently heard someone say, and I think it's roughly accurate, that writing lisp without paredit is like writing java without auto-complete (possible, but not very pleasant).

amalloy
  • 89,153
  • 8
  • 140
  • 205
2

I'm not sure you can avoid all the brackets. However, what I've seen Lispers do is use an editor with paren matching/highlight and maybe even rainbow brackets: http://emacs-fu.blogspot.com/2011/05/toward-balanced-and-colorful-delimiters.html

Frankly, these are the kind of features that would be useful for non-Lisp editors too :)

oblio
  • 1,519
  • 15
  • 39
1

Always use 100% recycled closing parentheses made from at least 75% post-consumer materials; then you don't have to feel so bad about using so many.

Kaz
  • 55,781
  • 9
  • 100
  • 149
1

Format it however you like. It is the editor's job to display code in whatever style the reader prefers. I like the C-style hierarchical tree-shaped format with single brackets on their own lines (all the LISPers boil with rage at that :-)))))))))))))

But, I sometimes use this style:

  (fn rep 
  ([lst n]
    (rep (rest lst)
    n
    (take n 
      (repeat (first lst)) )  )   )    )

which is an update on the traditional style in which brackets are spaced (log2 branch-level)

The reason I like space is that my eyesight is poor and I simply cannot read dense text. So to the angry LISPers who are about to tell me to do things the traditional way I say, well, everyone has their own way, relax, it's ok.

Can't wait for someone to write a decent editor in Clojure though, which is not a text editor but an expression editor**, then the issue of formatting goes away. I'm writing one myself but it takes time. The idea is to edit expressions by applying functions to them, and I navigate the code with a zipper, expression-by-expression, not by words or characters or lines. The code is represented by whatever display function you want.

** yes, I know there's emacs/paredit, but I tried emacs and didn't like it sorry.

Hendekagon
  • 4,565
  • 2
  • 28
  • 43
  • 1
    As long as you don't share your code with anyone, it's ok. But usually we collaborate on our code and then esoteric formatting like this becomes a huge issue. – Marko Topolnik Apr 11 '12 at 10:35
  • yes, but that's my point - if formatting were done algorithmically then it would not be an issue. The code is one structure, the formatting is another, everyone has their own way of seeing things, which is a good thing. What looks logical to one person looks hideous to another. The only reason we have formatting religions is because we are using old-fashioned text editors to view code, when really we should be visualizing code with an appropriate tool. – Hendekagon Apr 12 '12 at 00:01
  • My dissertation was about exactly this subject, so I had plenty of time to realize all the reasons why it's wrong. I thought text file was something of an atavism, but I don't think that anymore. – Marko Topolnik Apr 12 '12 at 04:58
  • 1
    interesting! I have been working on an expression-based Clojure code editor because I often get frustrated with the limitations of text-editors. Can you tell me what the main reasons were why you came to like text editors again ? – Hendekagon Apr 12 '12 at 05:52
  • 2
    Formatting code is more like formatting poetry than prose -- content and form are intertwined. Take lisps, they are among the more rigorous about formatting. You still decide on the line breaks -- and fret about it. More importantly, your very code changes to fit its formatting (choice of using local/inlining expr, choice of identifier to fit into the line, in Clojure choose when to use thrush operators, I even have some macros of my own to reduce indentation). Also, your canonical code format (the one holding 'content only') is subject to grepping, diffing, etc. – Marko Topolnik Apr 12 '12 at 07:17
  • 1
    all good points cheers. I will bear all that in mind when I next get the chance to work on my editor, particularly the point about switching between two conformations (inline or tree, thrush or embedded). However, what I would say is that nobody really agrees on the 'best' way to format code, it is a matter of personal taste and what looks like a logical flow for one person looks wrong to another. – Hendekagon Apr 12 '12 at 10:04
  • Of course, no contention there. The problem arises only when you open a file, change one line, reformat (with emacs) and save -- and end up with a 100-line diff due to your tool formatting differently than everyone else's. – Marko Topolnik Apr 12 '12 at 10:10
  • 1
    well, if code were compared at the expression level (post reader) then only meaningful differences would be found -- easy to do in Clojure, not so easy in C-like languages – Hendekagon Apr 13 '12 at 00:15
  • That's in the ideal world -- but in reality, the enormously rich and powerful toolset of today is based on the text file. You are _never_ going to see it all get reimplemented any other way and be as useful as it is today for the text file. – Marko Topolnik Apr 13 '12 at 07:21
  • @Hendekagon is correct. You can use an expression editor to view code however you like, and no matter how you are viewing it, the tool will always save it as a text file in a computer managed formatting style. Diffs will never be a problem again. In the various development teams I've worked in I'm seeing most people come to this conclusion. – intrepidis Jun 02 '13 at 11:26