Clojure:
(defn blub-extra [a b]
(blub (inc a) (inc b)))
8 parens + 2 bracketsScala:
def blubExtra(a: Int, b: Int): Int {
blub(inc(a), inc(b))
}
8 parens + 2 bracesJava:
Integer blubExtra(Integer a, Integer b) {
return blub(inc(a), inc(b));
}
8 parens + 2 bracesRuby:
def blubExtra(a, b)
blub(inc(a), inc(b))
end
8 parensPython:
def blubExtra(a, b):
return blub(inc(a), inc(b))
8 parens, one colonC:
int blubExtra(int a, int b) {
return blub(inc(a), inc(b));
}
8 parens + 2 bracesIt's roughly the same numbers of brackets (or equivalent) in Clojure, Scala, C and Java. A bit less in Python and Ruby.
def blubExtra(a: Int, b: Int): Int = blub(inc(a), inc(b))
The original would need an equals thrown in there, otherwise it's procedural syntax (IIRC, has been deprecated or will be in the next release) which has a return type of Unit, thus not compiling when specifying a return type of Int. def blubExtra(a: Int, b: Int): Int = {
blub(inc(a), inc(b))
}(inc was just a random function for the sake of showing invocation so it's not really fair to use operators)
The position of parens is something to get used to, but then again so is Python's indentation, Scala's type system, etc etc. Every language has something unique to get used to.
F#:
let blubExtra a b = blub (inc a) (inc b)
4 parens.
let blubExtra a b = inc b |> blub (inc a)
which 2 parens less, but at the cost of using the (very idiomatic) pipe operator.
S-expressions are uniform in that they all look the same way. This means you have one form for literally everything in the program and you will have no problem parsing that form. It makes reading, mentally parsing and editing easier because everything is neatly delimited by parentheses.
In terms of readability, do you have any particular difficulties parsing the following code?
(define (sum/recursive lst [sum 0])
(if (null? lst)
sum
(sum/recursive (rest lst)
(+ sum (first lst)))))
We are defining a function, sum/recursive, that will take a list and return the sum of all the numbers in that list.We use a default value of 0 for a function parameter called sum to store the sum. When the input list is empty ('(null? lst)' returns true) we return that variable. There is no return keyword, we just specify that variable and it will be returned.
If the list isn't empty, we apply sum/recursive on the rest of the list and as a second parameter we add the first number in the list to the already accumulated sum ('(+ sum (first lst))'). Using matching parens in your editor will make it obvious when you have matched the right amount of parens.
It should be noted that Racket, this Lisp variant, uses '[]' as well to delimit certain parts, for readability.
In any case, the bigger part of the being productive in a codebase is to figure out how it encodes the domain, what idioms and patterns are favoured etc. So sometimes, it's just not worth adding another source of aggravation for "new arrivals".
When you compare Clojure to other lisps, I think you'd agree it's more discriminable, given the plethora of literals that dont use parenthesis: vectors [1 2 3] sets #{1 2 3} maps {:key "value" :name "Bryan"}
I guess I need a clojure mentor for the first non-trivial project. There really isn't that much idiomatic web-related code out there to read, and with clojure there is also quite a lot of variation in how systems get designed (b/c of the power and expressiveness available).
This is not an excuse, just noting the factor that has turned out to slow me down. If i had more leisure time I'd just power through and build stuff until I arrived (through trial and error) at my own sense of best-practices / design tradeoffs. That has yet to happen but will someday.
If you treat the language as a user interface to your computer, shouldn't things that do different things have different appearances, to help you distinguish them at a glance?
Like you say, with training, the differences are probably not that big a deal, but I don't think they are something to completely ignore, either.
In most ways I care about, Clojure is actually safer than C#. Now, if you're coming from an ML HM language, sure, you'll be taking a step back perhaps.
That's why I come back to Clojure every time, if I need anything, it's available as a library. Need better typesafety? Just add in what you need with a macro or two. (Schema is not much more than that).
That's not really possible easily with Javascript, and Javascript has a lot more unsafe by default edge cases than Clojure. I'm not sure why if a mostly safe dynamic language bothers you, you'd now prefer an extremely unsafe dynamic language?
Lastly, types only give you a single dimension of safety, is this that exact shape, and ensuring I don't put a square peg into a round hole: both of which Schema adds in easily. Clojure is ALSO safe along many other dimensions, like thread saftey, immutably, null reference exceptions, etc.
> Lastly, types only give you a single dimension of safety.
Dependent types can express a huge number of things, and are often limited only by your ability to describe what exactly you want.
Dependent typing is some cool stuff, I'd love to see more of it.
There are some people using some of extensions for Haskell inspired by "dependently typed" systems in production.
Dependent typing is an active research topic right now, and I doubt any fully fledged dependently typed language is used "in production".
However, I was just pointing out that it is possible for types to express a wide variety of things that we do not normally associate with them. Even without full dependent typing, Haskell types are wonderfully expressive and powerful.
There are 3 concepts that are all diffrent, and a couple yours ago core.async was added. All in all thats a amazingly simple and powerful combination. Everybody I have heard is pretty happy with these tools. What is your problem?
Lisp languages in fact have the same(ish) amount of parentheses as any other language, the trick is that the placement slightly differ: the opening parentheses come before the function name, rather than after it. Ie foo(bar) => (foo bar) . foo(bar(baz)) => (foo (bar baz)).
(20 . > . (10 . / . 5))
may be more clear than (> 20 (/ 10 5)) [1] http://docs.racket-lang.org/ts-guide/
[2] http://docs.racket-lang.org/guide/contracts.html?q=contractsWith Clojure/Lisp languages you are basically starting from the beginning. Most of the approaches you would take to constructing your applications don't work. It just takes a lot of practice for you to recognise patterns and then both the typing and parentheses will seem natural.