>>> def deriv(f):
... dx = 0.0001
... def fp(x):
... return (f(x + dx) - f(x)) / dx
... return fp
...
>>> cube = lambda x: x**3
>>> deriv(cube)(2.0)
12.000600010022566 >>> def deriv(f):
... dx = 0.0001
... def fp(x):
... return (f(x + dx) - f(x)) / dx
... return fp
...
>>> cube = lambda x: x**3
>>> deriv(cube)(2.0)
12.000600010022566Does it just take some serious perseverance? Or am I just Lisp-dumb?
If I was to go back before that point, I'm sure I would find most languages more readable than lisp, but now that I know it fairly well, the syntax/parens don't bother me at all. I can parse it really easily with a passing glance. Maybe it's just a matter of experience.
In the end, a language is a language...if you don't like one, you can use another. I happen to love lisp, but it's a personal choice, and I can understand why people wouldn't like the syntax.
Whenever I bring this up among my colleagues, I get shot down by the proficient folks. The argument then turns into how I'm deficient somehow or haven't tried hard enough -- the word "stupid" came up a few times -- and that just completely turned me off the language.
Religion is a touchy subject.
But see orthecreedence's post. It can be learned with practice.
I can try to do something with it again if I have time, but I feel it's going to be a long and laborious process before I start to get comfortable. I used to unconsciously put semicolons ;)
(printf "%d" i)
Before I forget and switch back. I strongly suspect that the people who can't adapt are having difficulty because they are not programming with the new syntax full time. Practicing at night while the majority of your time is still spent with some other language is not the same thing. This is also true for learning human language and is why immersion programs are so successful.
Further, I absolutely do not buy the claims that people have that they can understand haskell easily but not lisp. Haskell might look more familiar at a casual glance, but its syntax takes a substantial amount of time to really understand.
I think this is spot on. I have a minor quibble that can't adapt ought to actually read find it difficult to adapt for the very reasons specified--it's not a matter of can or cannot, but a reflection of how much time one is actually able to invest in learning, like a human language. Random, sporadic investment into learning French vocabulary won't help much in improving one's ability to actually speak French. Much will be forgotten that way.
Immersion is definitely the best model for picking up any new [programming] language. Each language I use with measurable proficiency--Python, Objective-C, C#, JavaScript, French, English, etc.--is in that list because I actually took on tasks in which that was the language I had to use. Each language I've messed around with on the side for fun/experimentation/curiosity--Mandarin Chinese, Lisp, Haskell, Java, etc.--is barely worth mentioning.
That is very interesting. It took me indeed may months of night-time programming in Clojure and elisp to get at a point where I could feel confortable with the syntax. I had a series of small "enlightenment" during that journey: the last one was not long ago, when I realized how I could "carry" state through a high-order function.
But to stay focused I kept reading and re-reading great writing about Lisp by Paul Graham and kept viewing and re-viewing amazing videos by Rich Hickey.
It's as with everything: keep your eyes on the goal, not on the obstacles.
And at one point things are going to click in.
Of course Python makes it easier to figure out what code belongs where, it was made with this goal in mind after all.
I believe it is a tradeoff, Lisp's syntax gives you flexibility and an easy (in terms) and powerfull macro system at the expense of code readability (when compared with some languages).
so that say:
foo 1 2 3
bar 1 2
if (eq qux 3)
fizzbuzz 7
is translated by the parser to: (foo 1 2 3)
(bar 1 2)
(if (eq qux 3)
(fizzbuzz 7))It's never really caught on though, I would guess due to inertia. Most Lisp programmers just get used the parens, and maybe those that can't end up not using Lisp.
It would really open up the list world, especially to those of us comfortable with python.
It seems like such an obvious upgrade to me, since:
A: In no case will it break code that works now. B: It would always be possible to translate to and from it unambiguously, so if a team wanted to keep all of their code one way or the other, each individual developer could still see things with their own preference - imagine a tool like `gofmt`.
Once I make a serious foray into Lispland maybe that'll change...
Emacs' automatic indentation to The Proper Place is tremendously useful. I find Lisp's indentation to be just as easy as Python's, for example -- likely as I have used a Lisp for six years before coming to Python.
Once you've used it for a while, you don't think of it as much different from using {} for control blocks, () for method calls, and [] for array indexing in other languages. For me, the parens "go away", in that I follow the program control flow more by indentation than by counting parens.
((foo 1 2) 3)
((bar 1) 2)
would be foo 1 2
3
bar 1
2May I propose:
(foo 1 2) 3
(bar 1) 2I think the main annoyance is not the syntax per se but the fact that calls are way more nested than in an imperative language counterpart.
(setf new-links (append new-links (link-scanner (cu-body url-hash) (parse-uri cu-url url-hash)))))
That's a quick one-liner I wrote for part of a webcrawler. In Lisp you don't need to know anything much about parsing that syntax except that after an open parenthesis, you're going to have a function/macro followed by parameters.
Going by your example I could either do:
setf new-links (append new-links (link-scanner (cu-body url-hash) (parse-uri cu-url url-hash))))
or
setf new-links
append new-links
link-scanner
cu-body url-hash
parse-uri cu-url url-hash
or setf new-links append
new-links link-scanner
cu-body url-hash
parse-uri cu-url url-hash
In the first example I've hardly improved anything, just removed a single pair of parens.In the second example, I've split each new nested function call onto a new line and indented. This leaves "raw" parameters on the same line as the function call, whilst moving function params to the next line. (Note that Lisps don't make any distinction between the two usually).
In the third example, I've gone for some unholy mix where I've left the first nested function call on the same line as the first "raw" params, but moved the next params to the next line and repeated with any following functions. This is a mess...
Personally I think the 2nd example is actually quite readable. But here's the catch; this was 1 line from a 60 line function. If every line was multiplied by 5, is a 300 line function still as readable? And that's before you even get into any considerations of how to implement macros and other potentially complicated constructs.
EDIT: Admittedly, not every line is going to grow by a factor of 5, but it'll still be enough to turn a one page function into a multi-page function.
Lisp code is a tree, and emacs has a lot of neat commands to let you move around the tree (up one level, down one level, leaf forward, leaf backward), and I find myself doing this whenever I have a piece of Lisp code open to "feel out" its structure.
It's a little different, and maybe less like "reading" than understanding a piece of Python code, but I like it.
When we read code, we use our language abilities. Our ability to read syntax uses some sort of built in grammar recognition, which has a maximum capacity that it can handle and doesn't abstract well. Thus in "normal" programming languages a certain amount of flow of control is pushed into syntax and grammar, and the offloading of that work makes it easier.
Lisp goes the opposite way. There is pretty much no syntax, thus all flow of control is automatically pushed to your higher reasoning area, which suddenly does a lot more work and you notice it. Thus it feels like more work. The tradeoff is that everything is more flexible and easier to build abstractions on. But after you have programmed enough Lisp, the "flow of control" key words get wired properly into your grammar processing, and you stop noticing the effort again. But it takes longer for your brain to start processing things this way.
Something similar happens in normal language. The same grammar areas of the brain pay attention to pauses, speech emphasis, and so on. Which in written form we notice as punctuation. But it is also wired to pick up and process small connective words like and, not, or, etc.
Note, I have no evidence for my theory other than "it makes sense to me". However it is based on my limited knowledge of how brains process speech, which we have learned about because of the fact that when strokes take out specific areas of speech, we wind up with specific speech impediments.
Consider the formal grammar that might be used to implement a programming language. When we use the word 'grammar' here, it's obvious that Lisp has less of it than, say, Python. It's roughly equivalent to saying that Lisp has less syntax. So, his argument is independent of any VSO/SVO distinction.
Ben is arguing that Python's additional syntax, by formalizing common structures, allows us to (in some sense) externalize the inherent complexity of a problem (i.e. out of our brain). The downside is that it introduces some rigidity into the language. Lisp makes a different tradeoff: we are forced to handle all of the complexity ourselves, but in return the language is flexible enough that we can build exactly the right abstractions for our problem. There's a kind of conservation law.
So while I think that it's somehow wrong to always paint Lisp dialects as "having almost no syntax", I do also think that it's totally correct to say: "Lisp dialects have almost no syntax compare to mainstream language".
P.S: don't get me started on JavaScript where syntax gets inserted for you automagically, leading to bugs that can be very hard to track (re- Crockford on the error that inserting automated semi-colons in JavaScript was).
Just the LOOP macro:
http://www.lispworks.com/documentation/HyperSpec/Body/m_loop...
I wouldn't say that the VSO vs SVO distinction is Lisp vs infix languages. Because in an infix language I can and do write things of the form do_this(some_thing, various, parameters).
However it does come up with OO programming where we have some_thing.do_this(various, parameters). Versus the CLOS (do_this some_thing various parameters). People seem to prefer the standard OO syntax even though the CLOS approach is more general (since do_this will dispatch to the correct method based on its parameters, and can pay attention to more than one parameter to do so if needed).
(do-this-process object1 object2 .. object-n)
Like: (collide ship1 ship2)
or (render document1 printer1)Even though I started out with Algol-like languages I never liked all the different rules for braces, parens, comma's, square braces, assignment, variables, etc. etc. so Lisp was a natural fit for me.
Where is the (object)?
When was your last birthday?
Whither wander you? — Shakespeare
All of those sentences use inverted word order. One of the reasons that English is so expressive is that it uses both inflections and word order to determine the meaning of a sentence.One of the great things about Lisp is that it allows you to define your own infix operators.
"Where," "when," and "whither" are interrogative pronouns. They are the objects of the "to be" verbs. These sentences are actually OVS.
The one thing that all those languages drop from Lisp is Homoiconicy [1]. There are entire other categories of "Eureka Moments" that you can experience with Lisp through the lens of homoiconicy. This is at the heart of the utility of Macros, but macros can exist without homoiconicy and there are other benefits besides easier macros.
What makes Clojure, in particular, interesting is that it's Homoiconic in terms of more than just sequences: There's also maps, sets, etc. Common Lisp & Scheme encode maps, sets, etc in terms of lists.
If you're having a hard time seeing past the syntax (or a lack thereof), I suggest exploring Mathematica. Download the demo [2]. The Homoiconic parts of Mathematica are hidden behind more traditional syntax. Use the FullForm[...] function to see the Lispiness leak through. I suggest exploring the Rules & Patterns documentation [3] to get a feel for a non-macro approach to homoiconic transformations. Rules & Patterns makeup a rewrite system, which are strictly more complex than macro systems, but often easier to grasp during initial exposure.
[1] http://c2.com/cgi/wiki?HomoiconicLanguages
[2] http://www.wolfram.com/mathematica/trial/
[3] http://reference.wolfram.com/mathematica/guide/RulesAndPatte...
What do you mean by this? You do know that I can write some reader macros to get the same syntactic sugar for dictionaries and vectors as in Clojure, right?
The first has something to do with Functors, the second two something to do with Monads, and the last something to do with Arrows, and I have no idea what their precedence is. The first three all more or less solve the problem of "use a function I'd normally use on values of type `a` on values of type `m a`, where `m` is a Monad or a Functor.
The reasons this stuff makes code hard to read (for me) are:
1. They're all infix and I don't know the precedence.
2. It's not restricted to standard-library code. Oftentimes, learning a new Haskell library involves figuring out which part of the infix line-noise namespace it's staked out for itself.
Granted, Haskell people seem to have a sometimes unhealthy attraction towards having operators for everything. The Lens package in particular is rather awful in this regard IMO. (Fortunately there are non-operator equivalents to everything)
None of those have anything to do with the type system though. That's why I called your statement nonsense. They are just ordinary operators like + and -. The habit of claiming everything you dislike about haskell is somehow related to the type system is quite common and rather bizarre.
>The reasons this stuff makes code hard to read (for me) are
The reason is because you haven't learned haskell. If you had never learned arithmetic then 5 + 3 * 7 would make no sense too. That doesn't mean math is hard to understand, it just means you need to take the time to learn it.
>They're all infix and I don't know the precedence.
Use :info in ghci, or look it up on hoogle. Just like you would with a function you aren't familiar with.
:info (<*>)
infixl 4 <*>
Left associative, precedence 4. Addition is 6, multiplication is 7.>It's not restricted to standard-library code.
Lots of languages let you write new operators. If a library creates tons of operators that reflects on the library, not the language.
This is the same advantage as mathematical notation has over paragraphs of text: I can get the general idea from a formula or diagram without reading it in detail. In a sense, I can infer the "shape" of the notation, which is what lets me avoid actually reading everything.
Getting to this point with both mathematical notation and Haskell took a lot of practice, but it's well worth it: both notations have exceptional information density and allow me to go through more information faster.
Sometimes, for some completely foreign ideas, I do have to read the mathematical notation/Haskell code in much closer detail. And this does take much longer than you'd expect: a single page can take something like half an hour or more. But this is not much of a surprise: if the notation was expanded to prose, it would take up several pages, and not be an easy read by prose standards either.
I've had the same difficulty with Lisp, but I can read Forth and Postscript (another postfix language) easily. I am really not sure what that says about how brains get wired other than I encountered both postfix languages early in my career.
Maybe we don't realize how long it takes to acquire a new skill.
If someone had told me it would take a year to just become comfortable with emacs, I would never have picked it up. I already have sublime installed, and textmate!
But I picked it up because I respected people I knew who were wielding with great skill, and I wanted to try org-mode for TODO lists.
I'm glad I did.
(+ 1 2)
I was thrilled in the moment when I realized that all of the three parts of this banal form can be an arbitrarily complex forms themselves. For example, you can in place substitute "+" with a long code that contacts a web service and in the end, after 100 lines of code, returns "+". This 100 line function is made of forms made of forms made of forms and each can be replaced by the code that you might need. It's the beauty of the conciseness of the ternary operator in other language (a = b? 1 : 2) taken to a possibly infinite extreme.
This can sometimes be achieved in other languages with the use of temporary variables, one-shot functions if the language doesn't have lambdas (or multi-line lambdas like Python) and in the end litter your soul with the use of "eval"
This also leads to the other wow moment, when using Lisp makes it appear other languages' syntax so inelegant and cumbersome. At its core everything in Lisp is just like this:
(FUNCTION argument1 argument2 …)
When it clicks, it really hits you with the beauty of its perfect mathematical purity and you wonder how can you feel so comfortable with the average C-like language, where for has a syntax that is different from while, switch case is a beast of its own, sometimes you use curly brackets, sometimes square, sometimes regular or even angular, you don't have to forget commas and semicolons, you use colons and dots and question marks and myriads of keywords each with its own syntax and some things are functions and other operators and equal means something different from the double equals and so on.
To me, Lisp often felt like if the English language only had commas for punctuation. We then formulated all possible scenarios out of words and commas. Sure it's simple, easy for a computer to parse, etc, but it off loads a lot of work onto us as we read.
I'm also reminded of when I took a typography class in art school. My professor was adament about typography not being "grey". He really wanted us to use weight, contrast, spacing, to create and enforce a hierarchy within our typography which would guide and aid the reader. I'd argue Lisp is completely "grey", while other languages are not.
Don't get me wrong, this thread has been a great read and I've gained new respect from Lisp from it. I'm inspired to give it another go.
((lambda (x y) (+ x y)) 3 4)
looks much harder to read than this:
(function(x, y) return x+y end)(3, 4).
In the Lua version, you can easily skim the code: here is a function (rather than a Greek letter), the parameters are inside parenthesis and separated by commas, the body comes next until the "end" keyword, it produces (returns) a value and then you pass the parameters (inside parenthesis and separated by commas again) to the function that was just created.
In the Scheme version, how am I supposed to know where this lambda ends other than counting parenthesis and having a lot of practice to recognize that (....... thing thing) is a function call where the function is more than a single name?
Similarly this:
(define (fn x) (lambda (a) (+ a x)))
IMHO looks much harder to read than this:
function fn(x) return function(a) return a+x end end
The second one is much clearer: "return function", you are returning a closure to the caller. The Scheme one makes me think: a function, another function, parenthesis, an expression, then it ends abruptly and I don't know what the hell the code supposed to do. Again, I CAN read that code in Scheme, but only after learning the core concepts in Lua.
fwiw, it is immensely easier to build a parser for (= z (+ y x)) than for z=x+y
you might ask, why should that matter ?
Ad outside the eyeglass store: why do we have ears ? so we can wear spectacles!
The point being, we first evolved as creatures with ears and later on devised spectacles so we could wear them over the ears. But we've now taken the ears so much for granted we think of spectacles as the innovation! Parsing should have been the whole point, but we now prize readability. We then add fluffy monikers like "serious perseverance" and "Lisp dumbness" to further tilt the balance in our favor.
I will let the master explain -
http://lib.store.yahoo.net/lib/paulgraham/onlisp.pdf
Read Section 1.2 - "Change the language to suit the problem. In Lisp, you don’t just write your program down toward the language, you also build the language up toward your program. Language and program evolve together. Like the border between two warring states, the boundary between language and program is drawn and redrawn, until eventually it comes to rest along the mountains and rivers, the natural frontiers of your problem. In the end your program will look as if the language had been designed for it. And when language and program fit one another well, you end up with code which is clear, small, and efficient.
The greatest danger of Lisp is that it may spoil you. Once you’ve used Lisp for a while, you may become so sensitive to the fit between language and application that you won’t be able to go back to another language without always feeling that it doesn’t give you quite the flexibility you need.".
In Lisp, you never, ever have to worry about operator precedence because the order of operations is explicit in the syntax; you just start from the most deeply nested parentheses and work outward. I'd say that's a worthy reward for learning to read code in prefix notation.
(defmacro arith [& args]
(letfn [(res [v]
(cond
(vector? v) (apply arith-body v)
(list? v) (map res v)
:else v))
(arith-body [s & args]
(reduce (fn ([expr] expr) ([expr [op arg]] `(~op ~expr ~(res arg))))
(res s)
(partition 2 args)))]
(apply arith-body args)))
(defn deriv [f]
(let [dx 0.001]
(fn [x] (arith
(f [x + dx]) - (f x) / dx))))
((deriv (fn [x] (arith x * x))) 3)
Another common problem is the piping of results from one function to the next: (f
(g
(h a)))
where you usually want to start reading the code in the lower rightmost corner and go upwards towards the left. Very messy in many lisps.Clojure has solved that problem with the threading macros, yielding postfix notation when you need it the most:
(-> a h g f)
Piping subresults between functions really doesn't get much better in any language.And that's of course some of the appeal of Lisp: The syntax may start controversial, but you can choose most of it yourself and make it depend on the problem, your tastes and of course, your readers.
Infix notation is provided by macros or read macros.
Infix notation is provided by math packages on top of Lisp: Macsyma, Reduce, Derive, Axiom, ...
Those provide REAL extensive math syntax capabilities, not just what typical programming languages provide.
Piping operations like this can also be found in OCaml, F# and Perl6.
For eg. in Perl6 this is called a Feed Operator (http://perlcabal.org/syn/S03.html#Feed_operators) and your example would look like this:
a ==> h ==> g ==> f;a.h().g().f()
I feel that prefix notation is one of the greatest impediment to the uptake of Lisps. And I think it's important to underscore that you can (idiomatically) choose prefix, postfix or infix depending on the character of your problem.
If the code is (unnecessarily) hard to read, then express it differently. The language encourages it.
f(g(h(a)))
It can get kind of gnarly when any of the values are non-trivial.But when you have objects and no piping operator, library designers will often choose to implement pipe flow as method chaining. See e.g. jQuery, Scala's collections or many ORMs, like SQLAlchemy.
I doubt that there is any syntactical advantage though. In the end it is just a matter of opinion.
Everything looks the same. Some time ago I read that Common Lisp had multiple return values. I was expecting something like Lua, where a function like this:
function fn() return 1, 2, 3 end
Is called like this:
a, b, c = fn()
But in Lisp, it's just the same old
(some-magic-words-here (other-things) (other-things))
Could I have some commas and equals signs to separate things at least? :-(
Then I heard various Lisps have classes, structs, objects and what not. Does it look like this?
{ name: value, name: value }
Or this?
class Name { Type name; Type name; }
No...
It looks like this:
(def-something ((something-here)) (something-else) (((something-else-2))))
If you are lucky you have a few :keywords or &keywords so your eyes have something to hang onto. Other than that, everything looks so... positional, and a closing parenthesis could mean anything from the end of a simple expression like (+ 1 1) or the end of the declarative part and the start of the executable part of a complex structure.
You don't even need to go that far to find these problems: just look at:
(let ((new-var-2 value) (new-var-2 value)) (some-code-here))
(if (condition) (runs-if-true) (runs-if-false))
You'd better use a very consistent indentation, because there is almost no visual separation between the "runs-if-true" and "runs-if-false"!
The relevant wikipedia article is here, but it makes the process sound way more complicated than it really is: http://en.wikipedia.org/wiki/Automatic_differentiation
Here's a blog entry about it: http://nibot-lab.livejournal.com/65962.html?thread=108202
The first 3 chapters should look good in most languages with support for high order functions. It starts to get tricky in chapter 4 since Lisp is homoiconic.
I started writing about learning SICP using python [1]. Maybe I should continue it.
[1] http://pedrokroger.net/2010/08/sicp-in-python-1-1-the-elemen...
Stretching the analogy..., since Lisp isn't my native language, I very much appreciated the Python "subtitles" provided by the grandparent.
sub deriv { my $f = shift;
my $dx = 0.0001;
return sub { my $x = shift;
return ( &$f( $x + $dx ) - &$f( $x ) ) / $dx; }
}
sub cube { return $_[0] * $_[0] * $_[0]; }
print( deriv( \&cube )->( 2.0 ) );
12.0006000100226--------
"Lisp has all the visual appeal of oatmeal with fingernail clippings mixed in." -- Larry Wall (but see also http://www.perl.com/pub/2000/01/10PerlMyths.html#Perl_looks_...)
Here in perl5i (https://metacpan.org/module/perl5i):
func deriv ($f) {
my $dx = 0.0001;
func ($x) { ($f->($x + $dx) - $f->($x)) / $dx };
}
my $cube = func ($x) { $x ** 3 };
say deriv($cube)->(2);
And also in perl6: sub deriv (Code $f) {
my $dx = 0.0001;
-> $x { ($f($x + $dx) - $f($x)) / $dx };
}
my $cube = -> $x { $x ** 3 };
say deriv($cube)(2);I thought Perl 5 had an oversimplified object system till I saw Lua. In Lua, an object is just a hash, and there's a bit of syntactic sugar to call a hash element if it happens to contain code. Thats all there is. They don't even have classes. Anything resembling inheritance has to be handled by explicit delegation. That's a choice the designers of Lua made to keep the language very small and embeddable. For them, maybe it's the right choice.
ref: http://www.perl.com/pub/2007/12/06/soto-11.html
Also IIRC Wall's original Lisp quote starts with him praising Lisp and then ending with this infamous|humorous quip.
Personally, I think Lua is amazing. Rolling your own OO system in the language itself is pretty awesome, if you like that sort of thing. Everything has to be implemented in -something-, right?
Perl requires a blessed reference (which doesn't have to be a hash but often it is the best option!) which only contains the objects state. So unlike a Lua object hash the behaviour is maintained by Perl in dynamically scoped packages/subroutines (classes/methods).