Hacker News new | ask | show | jobs
by kvtrew76557 4566 days ago
Good question. Unlike most languages with which I am not yet familiar, Clojure looks like Greek. Most languages are at least somewhat readable. Perhaps I need to know Lisp to appreciate Clojure. Coming from a Java/Scala/Ruby/Python/Haskell/Pascal/C# background. I can't make head or tail of Clojure examples. It might as well be encrypted. To those who are using Clojure, which previous languages enabled you to make sense of Clojure more easily? Or was it something you learned from scratch?
14 comments

If you're really coming from a Haskell background I very much doubt that you "can't make head or tail of Clojure examples. It might as well be encrypted.".

Except if you mean that you dabbled in Haskell, but are a Java/Scala/Ruby/Python/Pascal/C# guy.

In any case, it takes no more than 1-2 days (from scratch) to get to understand functional code. Remember that you weren't born able to understand imperative code either. Just learn the (very basic) syntax rules, and the rest is easy.

This is so true - what interesting to me is that I have heard this sentiment directly from friends and coworkers before several times. Upon further prodding, it has been the case that their dealings Haskell were minor at best, and are confusing the superficially similar syntax of Haskell with languages they're used to, thereby giving them the idea they understand the code they're reading. But more often than not they really don't understand it - the brain is apparently adept at making us think we understand something we really don't based on a superficial, usually structural similarity with an existing knowledge domain we have.

I recall interviewing a potential hire who lauded his appreciation and understanding of functional programming - and he really believed it. So I asked him to explain to me what a closure was, and give me some examples of ways you can exploit them in your code, practically. Pretty straightforward question for someone who claims to understand the concepts of functional programming.

Of course, I wouldn't be bringing this up if he was even remotely successful, but I don't think this was a result of his presenting himself in a dishonest manner. I think closures are subtle ideas; much like function application, composition, and other ideas that seem familiar and easy to understand until you are asked to apply them practically. That's when the gap between what you think you know and what you actually know is borne for the world to see.

Closures are nice, but you don't have to understand them in order to understand functional programming ... Closures are more about how functional programming languages are implemented.
I find encapsulating mutable state within closures to be an indispensable and practical tool I use in almost every (stateful) program I write.
I don't know a Clojure. Why should knowing understanding one language imply that you automagically know another? If you know Spanish, Portuguese May look familiar but it won't be exactly the same. Does that mean you should understand Korean or Japanese?

From the little bits of dabbling with various lisps in have done a functional language is different enough from my bill paying regular languages (JS, php, Java) that without a focused study on a project that hits several layers of a typical stack, I fully expect the language to be foreign to me. Until I know the 350 core language operations by heart I'll not be able to play with them. If I cannot play I'll never be able explore the language and use and misuse it until I can make it do what I want it to do.

So the fact that it looks encrypted to you is a good thing. It's a code waiting for you to break it. It's a problem waiting. A small group of extremely intelligent people are responsible for the family of lisps. That tells me that if/when I do dedicate the time and effort to understanding the lisp tools it will be worth the effort.

The thing that helped me most when I started learning clojure is to mentally move the parenthesis over to the other side, so

    (println (max 34 64 15))
becomes

    println(max(34,64,15));
and vice-versa.

Another thing that helped is realizing that almost all things that require special syntax in other languages look like function calls in clojure. So, for example, to create a new function, you call the defn "function" and pass it parameters for the name of your new function, the expected arguments, and the body of the function.

The last thing is remembering that in clojure, the last statement in the body of the function is automatically the return value of the function, so I just imagined that the last statement had a "return" call in front of it.

With these 3 rules I could mentally translate 95% of clojure* to c-style code and back.

*The other 5% is mostly about macros, which are what give clojure (and other lisps) the power to add new features to the language via libraries, among other cool things. They they're powerful and used sparingly though, so you won't bump into them too much, and when you do, most will be documented as to how to use them properly.

> almost all things that require special syntax in other languages look like function calls in clojure

I would say that most syntax in other languages is there precisely so that not everything looks like a function call.

Good point. I suppose that the trade off is that you loose the "marker posts" from other languages that say to the programmer "Magic happens here, go read the docs" but you gain the power (together with macros) to add features to your language seamlessly.

In fact I think that only 13 "functions" are "magic" in clojure (as in written in java), and the whole rest of the language is written using those 13 functions[1]. That would be like, for example, adding golang's channels to ruby syntax by writing ruby code, without having to drop down to C, which i think is pretty cool.

I also found it very useful in real-life, mostly through having clojure libraries that can do things more seamlessly than similar libraries in other languages. For my last project, I needed to use maybe monad functionality quite often, and having it be as easy to use as the core language in my code was a huge win for my sanity.

[1]: I think some of the data structures are written in java as well, but only for performance reasons.

I find that those syntactic markers make it so much easier to parse the structure of code at a high level, and their lack (or the way they can be embedded inside other expressions) are what has always kept me away from a variety of languages, from Lisp and Forth all the way to CoffeeScript and Haskell.
If you already have a background in each of Ruby ,Python, Haskell and Haskell you probably can learn a good chunk of Clojure in a weekend, as it shares many features with these languages. The main difference, of course, is the prominence of lispy prefix function calls delimited by parens. Well, and macros, but Ruby does some of this.

Some links: http://blog.fogus.me/2010/06/09/clojure-rb/ http://stackoverflow.com/questions/4509782/simple-explanatio... http://jkkramer.com/sudoku.html

Unless you're a native Greek speaker. My point being that prefix notation is not something common in programming languages, but that doesn't mean it doesn't work or it's obscure: it's obscure to you because you're not used to read it, that's all. And yes, maybe the example is not best example.
What would objectively define "readability"? I hear this word thrown around a lot without much thought given to what it means. As far as I can tell whether a person considers a language "readable" is really only correlated with whether they were first taught a language with similar lexical syntax. People who were taught Scheme consider sexp based languages more readable, and people who were taught C/Java prefer semicolon delimited block-like languages.
When someone says "readability" I have come to realize they mean: "I can read now".
One of the reasons I think people may find clojure hard to read is that there isn't much vertical spacing. Do you find this translation of js to clojure any easier?

function doThis(arg1, arg2){

    var m = arg1 + 3;
    var n = modifyArg(arg2);

    if (m > 3){
        System.out.println("bigger!");
    }

    return m + n;
}

(defn do-this [arg1, arg2]

    (let 
    
        [m (+ arg1 3)
         n (modifyArg arg2)]
    
        (if (> m 3)
            (println "bigger!")
        )
        
        (+ m n)
    )
)

I'm not suggesting this is good clojure, but it's closer to the way imperative languages are written.

One thing which still causes me to expend a few extra brain cycles for me is the [] in the let clause. The fact that the locally scoped vars m and n are contained within a syntactic block, as it were, makes me feel that they're within an inner scope and not available to the code below. It's really trivial and absolutely a feel thing, but it does have small effect on the ease of readability.

This is why I like nimrod, which gives you the best both worlds

    proc doThis(arg1, arg2: int): int =
      let m = arg1 + 3
      let n = modifyArg(arg2)
      if m > 3:
        echo("bigger!")
      return m+n
With the added bonus that you get compile-time type checking (as well as a fair bit of type inference - notice I didn't have to specify any types other than the function signature), and the code itself compiles down to highly efficient C (https://gist.github.com/tylereaves/8116774 if you're really curious, do remember that code isn't intended to be human readable/modifiable).
Yeah, in thinking about my example a little more and generalizing, I think one of the problems that people have with reading clojure is that it doesn't have as robust a syntactic indication of scope that other languages have.
Don't you mean clojure has a MORE robust indication of scope than other languages? In clojure you can nest lets, and it is very obvious when the binding is out of scope, but in c-style languages, once bound, they stay bound till the end of the function.
Nope, Nimrod (at least) has full lexical scopes if you want

    proc foo(x: int): int = 
      let z = x * 2
      #z visible
      block:
        let y = z * 4 #z & y visible
      let foo = 3 #z and foo visible, y out of scope
I said "c-style", not ML style.
I don't find the example Clojure in the original article terribly readable, speaking as someone who writes a decent amount of Common Lisp.

My real entry into the Lisp world was via Perl and the higher order functions that I used there. That enabled me to grasp the overall semantic of Lisp; lots of time in the code did the rest.

Take a gander at the javascript/clojurescript synonyms cheatsheet, it's a lightweight way of seeing the mental transformations you need to do to get from a curly brace language to a s-expression based language: http://kanaka.github.io/clojurescript/web/synonym.html
I'm not a Clojure programmer, but I have an interest in things that run on the JVM. I went through this semi-quick guide to get an overview of how the language and syntax works:

EDIT: actually this was the link I meant to post. It was on HN a couple weeks ago: https://docs.google.com/presentation/d/15-7qFy6URdE7Owi2Litk...

EDIT: Not this link, though it looks interesting: http://www.innoq.com/blog/st/presentations/2010/2010-05-20-C...

You don't need to "know Lisp" in the sense of being proficient, but you need to be familiar with s-expressions. Once you grok s-expressions, the rest is "just" grokking a bunch of functional idioms not specific to Clojure.

Actually if you're familiar with Haskell then Clojure idioms should be more familiar to you. Clojure's datatypes are (almost all) immutable, driving it towards a lot of functional idioms you see in such as Haskell or maybe Scala.

Immutability is not the whole story. There is also parametric polymorphism, algebraic data types and pattern matching (best case analysis tool ever), type classes for type constructors (not just types), etc.
Of course. That said, Clojure doesn't have those, given that it has a dynamic type system and destructuring instead of pattern matching (not as clean, IMHO), so a comparison to Haskell in this respect is less useful.
My point was that it is perfectly possible to be familiar with Haskell and find Clojure (and Lisps in general) puzzling.

On the other hand, I would be genuinely surprised if someone familiar with Haskell were unable to pick up ML, or vice versa.

If you want pattern matching, check out core.match
Where is the exhaustiveness checking?
C code can be unreadable and sometime pure poetry. Lisps are the same. One difference though, s-exps are the most editable thing in the universe, paredit-like interaction makes you forget the superficial lack of readability.
In your case, I'd recommend you learn Scheme first. The Little Schemer and of course, the almighty SICP, will be of help.
Let me recommend DrRacket, it is an excellent IDE, and has great built-in documentation to explain what you can do. http://racket-lang.org/
I second this.

I really like Clojure, and it's what I started with in terms of Lisps. When I wanted to explore more "traditional" Lisp, I looked into Common Lisp, and it didn't thrill me. It felt old to the point of decrepit.

Racket feels very modern, and very polished. I'm trying to groom it as my go-to scripting language.

You're right, you might need to spend 15 minutes reading a basic tutorial or cheat sheet.