Tip #1 - spend more time in the repl than in the editor. The repl lets you figure out and grok the small things.
Tip #2 - compose the small things into bigger things. (And if you find yourself composing too many semi-complex expressions into on big expression, feel free to take any smaller grouping of expressions and turn them into a function.
You may start out very disoriented; but if you keep trying, at some point after trying (perhaps for a few evenings, consistently) it will just suddenly click. It's a wonderful feeling to suddenly get it.
One last tip - Everything is really just this pattern: open paren, operator, some number of parameters/data, close paren. I'm not a Lisp teacher, so I may be stating this inaccurately, but it's just that simple. The pieces may look like more than what I just described, but they are really that simple.
And on the topic of "too many parens", compared to Javascript, C/C++, Java and many other languages, you'll find about the same number of them. They're just arranged differently (and more uniformly in Lisp). Better yet, because they're so uniform, you can completely ignore/forget about the end of big expressions with their )))). Ignore it. Use an editor that matches braces, (I like rainbow color matching).
And now, a warning. If you take the time to really learn Clojure, there's a good chance you'll love it. Then you will find that in most companies, you won't be able to use it unless you sneak it in. Then you'll end up in Elixir because it "looks like" Ruby, and because everyone bought into Rails. But you'll still wish you were using Clojure.
Exactly. Even more, if you start counting {}, <> and []...
Also, before someone says "but prefix notation!", all mainstream languages are prefix too everywhere except math. That is,
frob(barify(foo, bar))
is prefix notation, and equivalent Lisp is achieved by just shifting the opening paren to the left of the operator name (and dropping commas, thanks to the uniform syntax): (frob (barify foo bar))I too get a lot of Rails spam from recruiters which I find hard to fathom since Rails usage is on the decline. What's your take on this? Is it that Rails shops will only settle for senior developers?
But indeed, Elixir is so different from Ruby that one doesn't just casually move to it from Ruby without learning to think functionally.
To your question about Rails, I'm not sure I can say whether it's in decline or not. But I do know that many hiring managers are not technical enough to know what's important, so they try to replace exiting employees with people who have the same skills. And probably there are one or more Rails apps that still need dev/maintenance. As far as money goes, Rails is still a very lucrative career focus, at least in the US.
I feel your pain here and just wanted to let you know how common this is for programmers who initially try out Lisp for the first time. As others have alluded to, I promise this initial "parenthesis shock" goes away rather quickly as you read and write Lisp code. In my direct experience, I have hired junior developers straight out of a coding bootcamp who were able to pick up ClojureScript in their first week using Sublime Text with no editor extensions. In other words: you can definitely do this; it's not magic.
I would strongly encourage you to check out Parinfer [1]. Parinfer makes indentation "significant" so you never have to worry about "closing the parens". Basically, if you can handle Python or CoffeeScript indentation, you can handle Lisp. There are no hotkeys to learn and it is available in most common editors today.
For a first project I would recommend rewriting something you have already written and are familiar with. That way you can focus on just the language and not worry about the problem domain or "how to solve it". Just do a 1-to-1 port of something you've already done and compare how the two solutions are the same or different.
I'm sorry you are being downvoted and being told to "just use emacs" or "use the REPL more". IMO, these are not helpful suggestions for someone new to the language.
Good luck. You won't regret learning Clojure. One of the best things I have ever done for my career.
If you need the visual differences between those various kind of parentheses, many lisps and schemes allows to use and mix any of those.
If you are confused because the parentheses are not in the usual place, then I think you are trying to solve problems that are too trivial if such a tiny bit of syntax get all the attention. Or, that you are trying Lisp on problems that are not a good fit. Obviously, lisp (or ML, for that matter) shines at manipulating symbols, not bytes.
I do not personally believe that lisp is, in general, "the most powerful language", but there is a problem where it can be said that it is the most powerful language: it's to write a Lisp interpreter.
Maybe that's the good starting project you are looking for? If so, I can't recommend enough the book "lisp in small pieces".
This is true a lot of the time. Also, commas don't exist in Clojure.
So you might have this in a C-like language:
if ((foo > bar) && (bar > boo)) {
doSomething(a,b,c);
} else {
doSomethingElse(d,e,f);
}
And in Clojure: (if (> foo bar boo)
(doSomething a b c)
(doSomethingElse d e f))
Another interesting side effect is that in C-like lang, one my say that due to precedence rules, (foo > bar) does not need the parenthesis since && is lower precedence. Yet, if you weren't sure, you'd have to take a short break and go look that up to be certain. Most devs will just keep the parenthesis instead of take that little break, or they will write them in anyway for readability. In Lisp, precedence rules are never an issue because all operators and functions are applied the same way. So the parenthesis might seem cumbersome at first, but they quickly make syntax quite a lot simpler. if(foo > bar && bar > boo) doSomething(a, b, c)
else doSomethingElse(d, e, f)
> Yet, if you weren't sure, you'd have to take a short break and go look that up to be certain.If you don't know how logic works then why programming? Also, this is not a "complex rule", this is just BASIC LOGIC. As obvious as it can be: read it aloud and you have it.
Also, what would this even mean:
(> foo bar boo)
THIS is what we need to check out because it's everything but obvious.> So the parenthesis might seem cumbersome at first, but they quickly make syntax quite a lot simpler.
It won't, it'll just make the code monoton and harder to look at.
Logic and precedence rules are two different things. I know plenty of Haskell devs who frequently consult not just the precedence but also the associativity (right/left) or operators or functions when reading or writing their code. C++ also has a very long list of precedence rules. It's not trivial and many find the lack of these added complexities an advantage in the simple lisp syntax.
> It won't, it'll just make the code monoton and harder to look at.
True, only if you haven't written much lisp.
I suspect you are trolling here, possibly, so maybe my feedback won't matter.
> (> foo bar boo)
>THIS is what we need to check out because it's everything but obvious.
Any symbol or word in Clojure (or another Lisp) following an open parens is a function call. You literally learn this in the first 2 minutes during the explanation of prefix notation, e.g. (+ 1 2 3) equals 6 or (- 10 1) equals 9. The great thing about Clojure is that > is just a function, not some special notation, so if you are in doubt you can use your editor to click through to the get the function definition (and the actual code). It isn't very hard to train your mind to treat any sequence of (foo x y z) as a function call.
So what does (> foo bar boo) mean?
or, put another way, the arguments must be in descending order
It's like instead of writing 1 + 2 + 3 + 4 in Clojure, you write:
(+ 1 2 3 4)
Less characters to type. Because operators are functions, which are always first in a list, normal binary operators like + can take many arguments.Another benefit of this is function application. If you have a vector/array of numbers and you want to add them all up, just apply them as arguments to +:
(apply + [1 2 3 4 5 6 ...])
If you had an array some-array that had hundreds of numbers, this would be trivial without doing anything other than using the same basic + function: (apply + some-array)
And since you are expressing the arguments as a single piece of data here, and you can have functions that are much more interesting than +, you can see where this leads.... building up arguments as data, using them in function application. It gets very interesting very fast and the language unlocks a lot of ideas you just don't get in other languages.I also find 'Less characters to type' a weak argument at the level of a handful of plus signs, but I grant that once you've made the mental leap there is less cognitive overhead. In the '(> x y z)' if I knew to read '>' as 'is descending' the meaning would have been obvious, but reading it as 'greater than' didn't make sense to me.
user=> (doc >)
-------------------------
clojure.core/>
([x] [x y] [x y & more])
Returns non-nil if nums are in monotonically decreasing order,
otherwise false.
So (> foo bar boo) essentially checks if bar is between foo and boo.If you instead want to compare or sort other values, Clojure offers other functions for that purpose where you can specify in great detail how things should be compared. If > could operate on strings, for example, what would be the expected result? The shorter the string, the "lesser" the value, or the string that starts with the "lower" letter, etc... it's quite ambiguous. You need more than < or > for that kind of comparison.
You may ask what does the function > do when called, but that's a different question and has nothing to do with a language's syntax and semantics.
Also, it's exactly the same with non-Lisp languages. If you don't know Python and see a fragment of code like this:
2 ^ 3 ** 3
wouldn't you have to ask the same question, ie. what do the operators `^` and the other one do when used? However, in this case, you need to also ask about the precedence of the operators, which doesn't exist in Lisp.You never "use your mind" to keep track of parentheses in any language - you use the editor's features for this, and read code structure by indentation like any sane human being does (also, it helps that I touch mostly Python ;) ...but for other languages you just configure your editor to reformat you code on every save or on linting). Everyone can get over "parenthesis madness" after configuring their editor properly and a few days of habit. Also, once you learn about "paredit mode" editing, if you like it, parens become a superpower, because you're not "typing code" anymore, you're just using your editor's shortcuts to edit the abstract syntax tree as a tree object that it rightfully is, not an ugly bucket of "code text"...
If you're sincerely not trolling, then Lispers will be glad you don't join in and come and soil their codebases with miss-indented or unnecessarily over-nested because of bad structure code...
Clojure improves on this compared to other lisps because it uses brackets and squiggly braces instead of parenthesis for a lot of its syntax, making that aspect much easier to read.
Just stick with it, you will see it is a non-issue after you get started in earnest.
Break big functions with deep nesting into smaller functions. (Like in any language). If you have to page up and down several screenfuls to check whether multiple blocks are at the same indentation level and under the same parent, maybe the function is too big.
I'm linking it because it has a couple of screenshots, which I think works better than just reading a description of how it should look like.
There's a standard way to indent lisp (the emacs way, which everybody copies) and as long as it's indented properly you read it like a whitespace significant language (Python, F#, etc).
Clojure is a bit easier to read than other lisps because it uses [] for vectors, arguments, and assignment blocks and {} for maps and sets. Gives the code a bit more texture.
People writing lisp day to day use an editor feature to automatically keep parens balanced (Paredit, Parinfer). Not required since there's a learning curve but it makes editing smoother if you're going to write lisp full time. A text editor that highlights the opening paren when the cursor is on the closing one (pretty common feature) is all you really need.
So in addition to additional symbols for notating vectors, map, etc, it uses fewer parens in general.
try using Atom with Parinfer plugin. i vehemently disagree with people trying to push emacs on Clojure/LISP newcomers. having to learn a possibly completely alien editor to be effective programming LISP is another big thing that pushes people away.
Firstly, you have an editor that matches them up for you.
Secondly, you read via indentation and whitespace. And since just about the only thing defining the syntax of lisps is the list structure, the editor also parses and can take care of that relatively automatically too.
I found it took about 6-12 months of exposure, and then your brain just tweaks/snaps/breaks magically one day, they all disappear, and then you no longer see them :P
>The only difference in readability is that the Java version has a lot more parentheses.
Clojure has a rich set of functions for manipulating data such as map, filter, reduce, and so on. There are also libraries such as Specter https://github.com/nathanmarz/specter that allow you to declaratively transform complex data. I highly recommend watching this https://youtu.be/rh5J4vacG98 short video to see how it works.
for example learning to use folds or transducers. Learning to avoid mutation etc
However I question generalizing from 'this was not an issue for my team at my company' to 'this should not be a concern for any company under any given constraints'.
I don't doubt most devs wouldn't have problems learning these concepts. It's whether business have capacity to allow those devs to onboard in such a fashion, knowing that different business are subject to different constraints.
In practice, the language is a small part of what a new hire has to learn. Each company has different processes, tools, workflows, frameworks, and so on. A language is only a small part of that.
While there are obviously companies where Clojure wouldn't be a good fit, the reasons wouldn't be that it's hard to train new people to work with it.
(function x y z)
let a = 123, b = 456, c = 789;
# more code
vs: (let ((a 123)
(b 456)
(c 789))
; more code
)
In many cases outside of function calls, there are more parens. {
let a = 123, b = 456, c = 789;
# more code
}
Because Lisp let explicitly defines its scope, while the first example inherits the scope from whatever it is embedded in. (let [a 123
b 456
c 789]
...)
Clojure syntax was designed with a clear and deliberate attempt to remove many of the parenthesis from lisp syntax. This is just a part of the greater design goal of offering very concise and elegant syntax in general, which is one of the reasons I keep coming back to it.I can create a great tool in Ruby for example, but asking a computer illiterate to run it in Windows isn't going to happen.
You can however hand that person a .jar from Clojure and ask them to run it.
On Linux, you’re even able to compile most of it into a single binary.
My example has 2 parens.
Even if you're not excited about the JVM, ClojureScript is particularly productive for front end development, where I genuinely love it tremendously.
The harder part about lisp is actually recursion. Surprisingly, there aren't a lot of programmers who can follow recursion naturally.
(loop exists in lisp but recursion seems to be the common idiom)
All examples of recursion I've ever seen in Lisp follow a similar structure...
* First section checks if recursion is finished. If so, return final result (or final element of full result if returning a list).
* Second section performs an action or two on the inputs.
* Third section calls the function again with revised inputs.
In many ways it's just a do while loop with different syntax.