I sure hope the giant, hideous, obtrusive diamonds inserted into the text to denote a hyperlink doesn't catch on as a trend. It's a great way to break the flow of the text and irritate your readers.
As for the idea of Lisps, well, it sure seems neat. But I've literally never run across a situation where I needed my code to edit itself. I've never run across a situation where the lack of an everything-is-an-expression-is-a-list feature prevented me from doing what I wanted to do.
So I just don't really feel the need to get repetitive strain injuries in my pinky from reaching for the parentheses all the time.
> As for the idea of Lisps, well, it sure seems neat. But I've literally never run across a situation where I needed my code to edit itself. I've never run across a situation where the lack of an everything-is-an-expression-is-a-list feature prevented me from doing what I wanted to do.
This is classic Blub paradox. You don't feel like you need a feature until you start using it, at which point you start wondering how anyone can live without it. Tools you have available limit the thoughts you can have. That's why it's always good to look for better and more powerful tools :).
OK, but I've been aware of Lisp macros for a while, and still never seen a situation where I needed them.
A couple weeks ago, though, I found myself in a situation where the user needed to be able to specify a filter, and the filter was going to be a tree of expressions, and the program had to take what the user specified and turn it into something the program could execute... and the filter tree looks a lot like an S-expression... hmm...
So I'm seeing something that could be done much easier in Lisp. It's still not a need for a macro, though (unless you're going to suggest that I use a macro to turn some user-writeable DSL into Lisp, and sure, it could be done that way.)
I'm not a lisper either, but I think the key idea, and I've looked but can't find the reference for this, is expanding the language space to intersect the problem space. Any given language has a domain of problems it's suited to: manipulating number, strings, objects with loops and conditionals and organizing code with data (oop). This is the language domain. Then users (programmers) create abstractions with the language to represent a problem domain (payment processing, cms, reddit, whatever).
The idea with macros is to extend the syntax of the language to move the entire language to the problem domain, for example the html generating dsl pg and company used at viaweb (see his writing).
This distinction is not as profound today, imo, because languages have come a long way. Php didn't exist when pg and friends were doing viaweb, today templating html is old hat, but at the time specifying a dsl in lisp macros to dyanmcically generate html was quiet innovative.
Everyone here is hoping their favorite language feature will added to Java 9, Python 3, C++14, Javascript, etc. Whatever feature that is, Lisp programmers would add it with a macro and move on. There is no central governing body deciding what I can or can't do with the language syntax. That's why macros are useful.
As someone who writes Lisp (well, Clojure) every day and does not particularly enjoy it, I find the common complaint about parentheses to be a non-issue.
In fact, I'm not sure I've heard of anyone who wrote any significant amount of lisp code and came away talking about parentheses. This seems to be mostly a reaction from people who've read a bit of Lisp without using it.
Then again, I just noticed that I type ( and ) with my third and fourth fingers, so maybe it would be worse if I typed properly :)
I would assume it'd be the same as people complaining about significant whitespace in python. No one who actually programs in python complains about the whitespace.
I developed in Python for several years and I disliked whitespace, because sometimes I want the flexibility to format code in a way that makes more sense given the right context and the whitespace was always in my way.
Plus, its whitespace-based syntax was used as an argument to not evolve the language, being one reason for why they haven't added proper anonymous functions.
I can't comment on LISP's parens much, for now it doesn't bother me, but Python's whitespace did and I tried liking it for about 3 years.
Python has proper lexical closures in the form of inner functions. Can some please enlighten me why one would still insist on multi-line anonymous functions? For documentation purposes it's a) better to give something a name, b) have a multi-line function in a separate place instead of inline in the form of a lambda.
Concerning whitespace, serious (large) projects have a very specific style guide, which includes prescriptions on whitespace. Python doesn't add any extra restrictions to that and in other languages a mismatch between whitespace and braces is a frequent source of bugs, so significant whitespace avoids that as well.
Only by not working with anonymous functions can anybody come up with such an impression.
Python has at least 3 features that are not needed in languages that have proper support for anonymous functions and that are more expression oriented:
1. for comprehensions
2. the with statement
3. decorators
You cannot work efficiently with higher-order functions until you have anonymous multi-line functions, period - also, Python's single-line lambdas would be a lot more useful if Python wouldn't have been so statement oriented, unfortunately in practice they are useless.
In regards to your points:
a) no, I don't buy that
b) ordering matters, we read code as we are reading text, top-down, left to right
> Python has at least 3 features that are not needed in languages that have proper support for anonymous functions and that are more expression oriented:
> 1. for comprehensions
Off the top of my head Scala, Erlang, and Haskell -- all of which are "more expression oriented" than Python (and all of which have robust support for anonymous functions including multiline anonymous functions -- the latter despite, like Python, having indentation-sensitive syntax), all have comprehension syntaxes like Python's for comprehensions.
So, I'm not entirely buying the idea either that "being more expression oriented" or "having proper support for anonymous functions" eliminates the utility of comprehension syntax.
Well, Scala and Haskell's comprehensions are monad comprehensions, mapping to operations such as map, filter and flatMap/bind. Python's for comprehensions only work on things that are iterable, which IMHO is a severe design limitation and makes them less useful than they should be. Think at Async I/O abstractions, like futures / observables / iteratees, which are not iterables.
And yes, if Python makes it easier to work with higher-order functions and such combinators / operators become the norm, then we'll talk about 2 non-orthogonal and conflicting features.
Python is the only language I know that added for comprehensions before proper support for anonymous functions. All the other languages I worked with (including Clojure, to be on topic) had anonymous functions before the syntactic sugar built on top. Clearly Python has a problem here.
> Python is the only language I know that added for comprehensions before proper support for anonymous functions. All the other languages I worked with (including Clojure, to be on topic) had anonymous functions before the syntactic sugar built on top. Clearly Python has a problem here.
Wait, first you claimed that Python wouldn't need comprehensions if it had better anon function support, and now you've claimed that Python has a problem because it added for comprehensions before anon function support, even though languages with anon function support still find the need for for comprehensions.
You seem to be convinced that Python is wrong, but not really committed to any consistency in the reason that Python is wrong.
You can live without for comprehensions if you have good anonymous functions support. If Python adds better anonymous functions support, its for comprehensions will be conflicting and much less useful than in languages that had good anonymous functions support from the beginning.
Either way, Python will never get multi-line anonymous functions support, since it's first of all considered to be un-pythonic. So we're having this discussion for nothing - I fell out of love with Python some time ago, if you still like it than good for you.
> You can live without for comprehensions if you have good anonymous functions support.
You can live without either, as decades of C programmers have demonstrated. Both are beneficial, as theeir widespread popularity in newer language attests. Neither is a perfect substitute for the other, as the fact that they tend to be both present in many newer languages, rather than being exclusive.
> its for comprehensions will be conflicting and much less useful than in languages that had good anonymous functions support from the beginning.
I don't see the "conflict" asserted here. I've used Ruby fairly heavily -- which has, in the relevant sense, far better anonymous function support than Python (even though it has different quirks) -- and certainly as nice as Ruby blocks are, a clean comprehension syntax is pretty much the main thing I find myself wishing I had sometimes in Ruby that Python has.
And, sure, Python's comprehensions may be less general than, e.g., Scala's, but switching them to be monadic rather than iterator based doesn't require better anonymous function support, it just requires changing which protocol they depend on.
Can you please explain to me what's important about these functions being anonymous? Why, specifically, they shouldn't be given a name?
How do you define working "efficiently with higher-order functions"? Given that Python fully supports higher-order functions, I am really curious what you could mean. I didn't downvote you, but it may have to do with your pointed assertion here, without anything in the way of an argument.
As to "ordering matters"; sure, but as the functions a nontrivial program calls are inevitably described as a graph, they must necessarily be defined in some arbitrary linear order anyway.
These functions are tiny and trivial. They don't need names, and if you were to give them names, the extra weight becomes burdensome. Not just in syntax duplication, but the redundancy of the name as a comment on the trivial function body.
nameIsFoo = x -> x.name == 'foo'
getAge = x -> x.age
some_collection.where(nameIsFoo).sort_by(getAge)
Note that giving the functions names has also changed the source order of the function bodies. Now you need to do a mental cross-reference to follow, instead of being able to read the definitions inline.
Sure, but those are one-line functions, which Python supports through lambdas. (Incidentally, some of them could be done with the operator module, without defining a new function). I would argue that once you write a multi-line function, it makes sense to give it a name and define it separately, which makes the "no multi-line anonymous functions" complaint less pressing.
I wrote an elegant parser definition library, used for parsing specialized output of ... never mind.
It had hierarchic specifications (regular expressions etc) of parsing states, along with anonymous functions which stored away parsed data and sent the parser among different states.
New juniors could after a few hours use this to quickly parse complex text documents.
I cursed a lot while trying to port this to Python. :-( A dozen named sub-ten lines functions specified by names and referenced inside a parser structure? Just didn't work.
Edit: The point is, there are use cases where real lambdas are useful (except from map etc). It is just weird to argue otherwise.
I'm a python guy and I totally agree that multiline anonymous functions greatly enhance the readability of code in certain applications. You can do everything with named functions, but it's not always the best method for conveying meaning.
I can live without multiline anonymous functions - but I'd make use of them if they did exist.
Imagine having to build functions with names for each while, if/else and foreach statements. Because that's what it feels like when working with async I/O in Python, a complete pain in the ass compared to other languages.
Scala sample:
cache.get[String]("name").flatMap {
case Some(value) => value
case None =>
database.query("names").head.flatMap {
case Some(id, value) =>
cache.set("name", value, 10.minutes)
.map(_ => value)
case None =>
Future.successful("Anonymous")
}
}
BTW, in this sample, for comprehensions are not that useful. But if you're using Scala-Async, you can write that in a style resembling blocking I/O:
async {
val cached = await(cache.get[String]("name"))
cached match {
case Some(value) => value
case None =>
val fromDB = await(database.query("names").head)
fromDB match {
case Some(id, value) =>
await(cache.set("name", value, 10.minutes))
value
case None =>
"Anonymous"
}
}
}
In both cases multi-line anonymous functions are leveraged.
> Can some please enlighten me why one would still insist on multi-line anonymous functions?
Reduced visual clutter and better flow in reading code.
> For documentation purposes it's a) better to give something a name, b) have a multi-line function in a separate place instead of inline in the form of a lambda.
I disagree: if the only place its ever used is in a particular call to a higher order function, it reduces the difficulty of reading the code -- if its not a large multiline function -- for it to be directly in the call as a lambda. Having it named is useful (1) if it needs to be referenced more than once (DRY), or (2) if it is large enough that it breaks up the flow too much for it to be included in one place (which is a somewhat subjective cut-off, but for me 1 line is far below it.)
EDIT: That being said, I'm mostly fine with Python the way it is -- while I sometimes wish a way to fit multi-line lambdas without disrupting the rest of the language could be found, I'm not sure I can see a good way for it to work, and its not really essential.
I find it a pain and error prone when having to change indentation in python code. E.g. when adding an 'if' in front of a block of code. So easy to miss a line or mess up the indentation in the block itself and then you might not spot an error until run time.
It's not a pain if you use an editor which lets you indent a whole block of code at once; e.g., in vim, use shift-V to select lines of code, then >> or << to in- or dedent. This is handy for all programming languages, of course.
I was under the impression that Python emits an error about inconsistent indentation at compile time (the initial parsing and interpretation of a script file), not runtime (+x time units later, after the program has started). Is that incorrect?
oops... and_this() should have been in the if block and I won't find out till I run it.
(imagine a much bigger more complex example of the above function)
In large pieces of code this can be easy do.
If you are forced to use parenthesis it's much more difficult to make this error. One could argue that experience prevents you from doing this but I have sadly found this not to be the case in practice.
> If you are forced to use parenthesis it's much more difficult to make this error.
By "parentheses" you mean delimiters, which create visible boundaries to defined areas in code. Parentheses are an example of delimiters, but not all delimiters are parentheses.
Bash has if ... then ... else ... fi
Ruby has if ... else ... end
C/C++/Java have (logical test) { controlled area }, nested to any practical depth.
And so forth. Python doesn't.
> One could argue that experience prevents you from doing this but I have sadly found this not to be the case in practice.
This is an argument against complex functions that do a lot, as opposed to breaking program logic up into smaller blocks that are easier to understand and control. The old argument against this practice was that a large function that did everything was faster than the same logic broken into smaller blocks. A modern compiler will generally prevent this from happening.
Well I used the word parenthesis since the topic was Lisp.
> This is an argument against complex functions that do a lot
Functions large and complex enough to make this problem significant seem to be the reality I have to deal with when programming in the large. It's only my opinion but a language feature that improves my real world experience at no cost is a bonus.
I fail to see your point. You could accidentally put a brace in the wrong spot, or use an if statement without braces in a C-like language, and it would stand out visually much less than the code you cite above.
Such errors are caught at compile time. I think the prior poster was speaking in a general sense in which runtime means any time after source file editing.
> No one who actually programs in python complains about the whitespace.
I wouldn't say "no one." I read comments regularly from people, usually students, who get into trouble with whitespace in Python, especially if they mix tabs and spaces in the same source file.
If you're mixing tabs and spaces in the same source file, chances are that you're not a very seasoned python programmer. I would imagine a large segment of students also complain about parens in lisp.
Though to be fair, there would be some people that use python day-to-day that don't like significant whitespace. I'd bet there'd be at least a few lispers that don't enjoy wrangling parens.
I program in Python for a living and I curse significant whitespace every single work day. Especially when I try to copy paste some code into the shell. It's one of the worst misfeatures of the language, and it wouldn't even be necessary if there was an "end" keyword.
I use IPython. %cpaste doesn't work all the time if you're copying from the middle of a function. And even if I just paste a one-liner that starts with a few spaces, why the hell does it even bother to complain about that? Oh wow, this one line of code (which happens to be the entirety of code I'm asking you to execute) is indented wrong, tell me something I don't know! What a PITA.
I program in python (and C/C++) and really dislike python's whitespace handling. I think giving semantic meaning to one of the least-standardized aspects of text (tabs/spaces) is a bad decision through and through.
Tabs should be used solely to remain consistent with code that is already indented with tabs.
Python 3 disallows mixing the use of tabs and spaces for indentation.
Python 2 code indented with a mixture of tabs and spaces should be converted to using spaces exclusively.
When invoking the Python 2 command line interpreter with the -t option, it issues warnings about code that illegally mixes tabs and spaces. When using -tt these warnings become errors. These options are highly recommended!
I feel it's actually easier with parenthesis because with always well formed lisp you get the benefit of incredible tools like paredit-mode which make it easier to type than in other languages and I say this as someone who does full time c++ with a fair bit of python, javascript and html and I only do lisp stuff as a hobby.
I was looking for a way to summarize lisp syntax benefits. Most BNF-heavy languages are statically appealing. They have layout and differentiators that makes it nice to look at.
Lisp is a building material, the expressions are objects, the syntax is object. You don't want to look at it, you want to model with it, live. And with the "metacircular" (sorry for the sophisticated looking lingo) mindset, you can program how you interact with it (protorefactoring ala paredit/redshank etc).
Most people can't judge s-exp properly because they're not playing with it only looking at dead printouts; and the few who does are using a textbuffer[1], avoiding one important programming rule : automate everything. Lisp syntax has a simple and potent programmable API giving you a lot of power for free.
[1] I even watched a lisper using emacs without paredit at a meetup and it was painful.
I find it odd that people use their pinky to type parens. When my fingers are on the home row, '(' is above my middle finger, and ')' is above my ring finger. Typing them with my pinky would require me to contort my hand.
> When my fingers are on the home row, '(' is above my middle finger, and ')' is above my ring finger.
If your fingers are on the standard home keys (index fingers on "F" and "J"), '(' is between the middle and ring finger and on the normal (inward) path of the ring finger as it extends, ')' is between the ring finger and pinky and on the normal path of the pinky as it extends.
OTOH, if you keep your right index finger on the "K", which is less standard, then those become naturally on the middle and ring (and it may make sense for programming, but less for general typing -- the standard home position is based on where the letter keys are, but the extra symbols on the right are more used in programming than general typing, so moving the right hand one key out makes some sense.)
It's not odd... it's traditional touch-typing.
It should be your ring-finger for ( and your pinky for ).
Many people seem to type it the way you have just suggested (middle/ring) - myself included. I'm currently re-training myself to do that properly.. it feels weird at first, but I'm starting to get the feeling it will actually be faster.
If you've never had the possibility to do so before it's not surprising that you never felt the need to do it. Languages affect your way of thinking and implementing algorithms.
On the other hand, once you've tried the sweet honey of lisp macros, going back to C macros makes you sad. There's not a week that goes by without me being frustrated at the rudeness of C macros. Not to mention languages that don't offer a macrosystem at all. And the parens thing is a tired meme.
I agree with you for the diamonds though, very distracting.
The first macro system I learned was Clojure's. Only once have I used a C style macro, and that was in my assembly class.
I noticed the article mentions that some don't like calling syntax extensions macros because of the comparison to Common Lisp macros. To that, I say that's a crazy way of thinking. Macros are necessarily dependent upon the semantics of the language they are for, so anybody who thinks that all macro systems are identical are the people who probably wouldn't go out of their way to learn a language with a non-textual substitution macro system, so changing the name for that purpose seems foolish. On the other hand, syntax-extension or syntax-transformations are a more descriptive term on their own merits.
And yeah, the diamonds are silly. Better to just color the word.
"But I've literally never run across a situation where I needed my code to edit itself."
Well, "need" is a strong word. I mean, you could probably argue, in a philosophical sense, you never needed to write any computer programs in the first place (plenty of people go through life without doing so).
However, it is very likely there were times in your life where having your code edit itself would have led to getting a program working in less time, with fewer bugs, or better performance. Which of these benefits apply, of course, depend on the problem you were trying to solve.
"So I just don't really feel the need to get repetitive strain injuries in my pinky from reaching for the parentheses all the time."
Um, no. Rich Hickey has pointed out idiomatic Clojure requires fewer parens than the equivalent Java.
I mainly develop software in C# and Python at work, and I sure do miss some of the convenience that lisp-inspired languages offer. I'm responsible for the overall architecture of quite some large-ish enterprise apps. There's a reason why many Java and C# business applications have tons of MySuperAbstractFactoryProvider classes and use reflection-based look-up of metadata attributes obsessively. There's just no other way to provide a flexible framework that meets the changing and often arcane demands of our customers.
About macros, in the case of C#, what, if not a kind of macro, is the 'using' statement that works with IDisposables? If it's useful here, why not in other places?
giant, hideous, obtrusive diamonds inserted into the text to denote a hyperlink
I hadn't even noticed that they're hyperlinks, I thought something had went wrong with the text formatting or character set!
I've literally never run across a situation where I needed my code to edit itself.
Needed to is too strong a statement. I've never needed to write a macro (and in Clojure, its somewhat frowned upon to write macros when normal functions will do), but sometimes it saves you from a lot of working around limitations (and every language has limitations). It also allows things to be added to the language as libraries that otherwise would have to be built in - the majority of programmers won't need to do this, but if you do, its awesome knowing that its possible. Clojure's core.async is an excellent example (Go-style goroutine's, as a library).
I've never run across a situation where the lack of an everything-is-an-expression-is-a-list feature prevented me from doing what I wanted to do.
Of course not, but you can also do everything you want in assembly. What everything-is-an-expression-is-a-list gives you is 1) uniformity - everything works the same way, so it lowers the cognitive load; 2) simpler code - if you need a statement in an expression, you can do so, and other languages don't prevent you from getting the same result - they just take more code, or the code is more complex, or you use less-than-ideal constructs, or...
So you never need these things, but they make life more pleasant to have them.
I just don't really feel the need to get repetitive strain injuries in my pinky from reaching for the parentheses all the time.
I program in Clojure fulltime and I don't find that it has any more parentheses than a language like C++ or Java does. On top of that, anyone who programs in a Lisp for a while will use some form of paredit, which makes working with parentheses a breeze to the point where I find I type less than I do in other languages because jumping between parentheses, splicing parentheses-enclosed lists, wrapping things in parentheses and such tasks are a single keypress that just isn't possible in other less-parentheses-focused languages.
In short, just like significant whitespace in Python, parentheses are a non-issue in practice (after a short adjustment period).
But at the end of the day, to each his own. If Lisp doesn't do it for you, then that's fair enough - you don't have to use it :-)
Yeah, I remember once a friend complaining about all of the parentheses. I asked him to literally scroll to the bottom of the java file he was at, where it was about as many closing braces as I have ever seen closing parens.
I realize this is not necessarily the norm. But it was hilarious in context.
Echoing everyone else, basically, your other points are a bit shortsighted.
You never need code that edits itself - completely true. Completely missing the point. Most programming features fall into this category; you don't -need- them, you could always use assembly instead. The point is that once you get them into your brain as an option, problems that might otherwise be tricky or time consuming become much easier.
To that end, everything is an expression is a list...yeah, you don't need it either. But, assuming the above (that code that can edit itself turns out useful sometimes), imagine how easy it is to metaprogram when all your executable code is just a list, and you already know how to modify lists.
In either case, you don't -need- the feature, sure, but a moment's reflection might open up the possibility that once you fully grok the ramifications, and have it amongst your other programming tools, you'll find a good use for it.
As to too many parentheses, as someone else mentioned,
I don't see how that is supposed to be a valid excuse. Non-lisps tend to support operator precedence, reducing the number of parens significantly. Thus, the argument "other languages require the same number of parens" is completely false.
For a book typsetting program that's trying to produce html without a lot of writing overhead, some of those features are pretty great. Steve Yegge has a related blog post (the context is emacs):
https://sites.google.com/site/steveyegge2/the-emacs-problem
Being able to write new control structures can come in very handle. Imagine if your language of choice had exceptions/errors, but didn't have try/catch/finally. Now imagine you could just implement that structure as a command/macro. That's a rather extreme example, but it does highlight the power available.
Yeah, middle clicking on the page to start a scroll got weird as well.
In defense of why-homoiconicity-is-cool: it's useful to build pipelines of things in code.
Imagine if you had a series of functions that manipulate an audio signal. You could create a pipeline of these on the fly just by putting the functions in a list, and then reflecting the data against it.
You can imagine exposing this to a user - they check some boxes saying what features they want, and then the pipeline gets assembled for them.
This would be do-able but laborious in Java.
Javascript and iolanguage are homoiconoic language without the parens emphasis.
> Imagine if you had a series of functions that manipulate an audio signal. You could create a pipeline of these on the fly just by putting the functions in a list, and then reflecting the data against it.
As for not missing stuff from Lisps, I think opportunities start appearing once you start using it more. But I agree, it doesn't prevent you from getting things done.. I don't see any Lisp in the Go space (native compilation, great networking features), so I find Go more pragmatic at this time.
As for the idea of Lisps, well, it sure seems neat. But I've literally never run across a situation where I needed my code to edit itself. I've never run across a situation where the lack of an everything-is-an-expression-is-a-list feature prevented me from doing what I wanted to do.
So I just don't really feel the need to get repetitive strain injuries in my pinky from reaching for the parentheses all the time.