Live data from Hacker News

Why I chose Common Lisp over Python, Ruby, and Clojure

postabon.posterous.com

31–40 of 143 posts

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#31
post #19
post #17

Earlier quoted context omitted.

> Python's list comprehension is much more readable than using map/filter/reduce - at least for Python programmers For simple cases, yes. But I wouldn't say [each for each in [expensive_call(x) for x in seq] if cond(each)] is more readable than filter(cond, map(expensive_call, seq)) at least for functional-thinking minds. The level of thinking in abstract is different here. Now the problem is, some people see Python…

I know you all know this, but I feel like it bears mentioning that nobody is forcing you to use list comprehensions whether map/filter stay in Python's built-ins or not. They can be defined in around 3-4 lines of code each. Lisp aficionados, already accustomed to the bottom-up style of programming, ought to have no problem writing functions like these as necessary.

Yeah I know. Maybe "force" is not the right word ... probably "discourage"?

Actually only one line of code is enough for each of map/filter:

    def map(fn, seq): return [fn(each) for each in seq]
    def filter(cond: seq): return [each for each in seq if cond(each)] 
But seriously, what do you really gain by removing these two? Isn't that too ideological? I don't really see how un-Pythonic it would be to use map/filter instead of list comprehensions. The problem is BDFL's attitude seems to drive many FP-ers away, like the guy in the original post.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#32
post #10
post #4

The fact that he proposed removing map, reduce, filter, and lambda from Python 3. In the case of map and filter, is there any compelling reason to use either of those instead of list comprehensions or generator expressions?

A single, non-nested list comprehension or generator exp is basically map(filter). You need nesting to get filter(map). e.g. map(expensive_call, filter(cond, seq)) equals [expensive_call(each) for each in seq if cond(each)] but filter(cond, map(expensive_call, seq)) equals [each for each in [expensive_call(x) for x in seq] if cond(each)] note because of "expensive_call", it's inefficient (and silly) to do [expensive_…

It depends slightly on how you came to functional programming.

You can arguably trace Python's list comprehension syntax all the way back to setl, a "set-theoretic programming language", and the resemblance to mathematical set-builder notation is intentional; compare

- let B = { g(a) s.t. | a \in A and f(a) holds}

- B = [g(a) for a in A if f(a)]

If you're used to thinking in sets having to decompose into "maps" and "filters" is a speedbump; easy to do but nice to avoid.

Where list comprehensions really start to shine is making it comparatively trivial to pull from multiple source collections without a lot of ugly machinery:

    [{'widget':w,'sprocket':s,'location':l} for w in widgets for s in sprockets for l in locations if l.hasInStock(w) and l.hasInStock(s) and w.isUsableWith(s)]
...which is about where explicit map + filter start to become annoying. You can use:

    map(lambda i: {'widget':i[0], 'sprocket':i[1], 'location':i[2]}, filter(lambda i: i[2].hasInStock(i[0]) and i[2].hasInStock(i[1]) and i[0].isUsableWith(i[1]), itertools.product(widgets,sprockets,locations)))
...but to my eyes that is not only very ugly but just going by character count the # of characters given over to keywords (map, lambda, filter) instead of "what i'm doing here" is huge. Additionally use of itertools forces use of tuples for your intermediate values and thus the lambdas are gobbledegook until you get to the tail end of the statement and see that i[0] == widget, i[1] == sprocket, and i[2] == location. I could define some constants (WIDGET = 0, SPROCKET = 1, LOCATION = 2, etc) but now it's even longer.

It gets even worse if you try to be clever with the sequence of operations.

You might look at that definition and say lo! I can pre-filter out stuff not in stock at each location and make things more efficient. Naively you'd wind up with:

  map(lambda d: {'location':d[0], 'widget':d[1], 'sprocket':d[2]}, flatten(map(lambda l: list(filter(lambda p: p[1].isUsableWith(p[2]), itertools.product([l],filter(lambda w: l.hasInStock(w), widgets), filter(lambda s: l.hasInStock(s), sprockets)))), locations))) #nb must-supply-you-own-flatten-method
Under some circumstances that might be substantially faster than the previous approach. But compare the equivalent but you could have instead gone with:

    [{'widget':w,'sprocket':s,'location':l} for l in locations for w in [widget for widget in widgets if l.hasInStock(widget)] for s in [sprocket for sprocket in sprockets if l.hasInStock(sprocket)] if w.isUsableWith(s)]
So you lose a little flexibility in simple cases at in exchange for increasing the scope of what you can get away with as "readable" one-liners.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#33
post #27

Ok, so Rails is too heavy weight for you? What about Merb? Or Sinatra, which was designed for just that reason? Dismissing Ruby as language because only one of a dozen available web frameworks is too "heavy-weight"sounds a bit short-sighted to me.

I am not the OP, but I have a problem with Rails and alternatives: there does not seem to be a viable alternative to ActiveRecord around? I looked into DataMapper (I think that was it's name) which initially looked great. But then I could not find any information on how to use transactions, and also received no answers on the newsgroup. I guess I should have read the source, but I gave up at that point.

Since I hate ActiveRecord, I have now subconsciously decided that Rails probably isn't for me.

It's great that Sinatra is spawning lots of imitators for other languages, though.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#34
post #15
post #10

Earlier quoted context omitted.

A single, non-nested list comprehension or generator exp is basically map(filter). You need nesting to get filter(map). e.g. map(expensive_call, filter(cond, seq)) equals [expensive_call(each) for each in seq if cond(each)] but filter(cond, map(expensive_call, seq)) equals [each for each in [expensive_call(x) for x in seq] if cond(each)] note because of "expensive_call", it's inefficient (and silly) to do [expensive_…

Most times you are interested in doing the simple, e.g.: filtered = [x for x in seq if x>10] Python's list comprehension is much more readable than using map/filter/reduce - at least for Python programmers :) Anyhow, I really like Guido's decision on dropping these - it creates a cleaner language and forces people to think Pythonic when programming in Python.

[It] creates a cleaner language and forces people to think Pythonic when programming in Python.

The question is whether "Pythonic," as the community defines it today, is optimal in all cases. As an analogue, there have been enough talks about things the Java community considers to be stylistically optimal that look really horrible compared to implementations in other languages -- like Python. :-) Pythonic style should be a guide, not an edict, and should be deviated from or redefined when it makes sense. The examples riobard provided already show the syntax weighing things down, a situation where syntax should give way to a more functional style approach.

As for it creating a cleaner language, I try to approach this, as with all things, from the perspective of being an language-agnostic programmer. From that perspective, I do like the python syntax for simple things like what you defined, but under heavier weight mapping and filtering operations, the map/filter function call syntax seems a lot cleaner. There's nothing wrong with syntactic sugar, but I would assert that it will tend to suck when it is all you have.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#35
I forget where this came from but this applies 100%:

You ask a person to rate 5 paintings 1-5. You then tell them that you have spare paintings that they happened to have rated as 3 and 4 and ask them if they would like one. They will take a "4". Come back in a week and ask them to rate the paintings again. The "4" becomes a "5" and the "3" becomes a "2".

Our brains will rationalize decisions to make us happy (whatever that means).

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#36
post #12

Basically: I started with my conclusion, and then went through all the other options and rationalized them away until it seemed like my choice was based on objective reasoning.

I think you drawing that conclusion may be a result of how I structured my essay (I introduced my conclusion, then my reasoning). I freely admit my personal biases and experiences eliminated a lot of potentially good options without ever giving them due consideration (e.g., Haskell, Scheme, Lua). But I really did try to give the 3 options listed in the article a fair shake at the time. For what it's worth - I was abo…

I too thought the logic was weak. Here was my basic objection: you chose not to use two popular web programming languages because of their potential volatility, but then went with one that has been so infrequently used in web programming that it almost certainly has opaque deffeciencies. It's not that your reasons were bogus, they just didn't carry the decision.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#37
post #19
post #17

Earlier quoted context omitted.

> Python's list comprehension is much more readable than using map/filter/reduce - at least for Python programmers For simple cases, yes. But I wouldn't say [each for each in [expensive_call(x) for x in seq] if cond(each)] is more readable than filter(cond, map(expensive_call, seq)) at least for functional-thinking minds. The level of thinking in abstract is different here. Now the problem is, some people see Python…

I know you all know this, but I feel like it bears mentioning that nobody is forcing you to use list comprehensions whether map/filter stay in Python's built-ins or not. They can be defined in around 3-4 lines of code each. Lisp aficionados, already accustomed to the bottom-up style of programming, ought to have no problem writing functions like these as necessary.

If I remember correctly, "removing" these functions mostly just meant dumping them into the functools module rather than including them as a built in function.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#38
post #16

Did you really choose Lisp over alternatives? Before learning CL I was a fairly decent, C, C++ and Perl programmer. Did assembly, Pascal, TCL and Awk. Up to that point, I always had to pause a for a minute when starting a new project/script, think about its scope, and choose a language based on the necessary performance, development speed, expressiveness, available libraries, etc. (and whether whoever was going to re…

Heh, funny you learned from the Winston & Horn book - that was my first too.

The biggest philosophical problem I have with Lisp (and a lot of dynamically typed languages) is the lack of compile time safety-nets (it really bites me when I'm trying to work with a large, distributed team). I miss interfaces/abstract-classes, type checking, etc. I know they aren't necessary - but I'm human and make more than my share of stupid mistakes (and I prefer my mistakes be caught at compile time). Unit testing/etc alleviate the pain to the degree - but it requires a lot of discipline to maintain good coverage.

The real trick is that I'm probably faster in Lisp than anything else, but I've met Python and Haskell hackers who are just as fast as I am - so it must be possible to use those languages well (you might argue that they'd be even better in Lisp ;-)).

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#39
post #4

The fact that he proposed removing map, reduce, filter, and lambda from Python 3. In the case of map and filter, is there any compelling reason to use either of those instead of list comprehensions or generator expressions?

map, filter and reduce were not removed from python3.x, however. They'll be there for the next decade at least.

Re: Why I chose Common Lisp over Python, Ruby, and Clojure

#40

I forget where this came from but this applies 100%: You ask a person to rate 5 paintings 1-5. You then tell them that you have spare paintings that they happened to have rated as 3 and 4 and ask them if they would like one. They will take a "4". Come back in a week and ask them to rate the paintings again. The "4" becomes a "5" and the "3" becomes a "2". Our brains will rationalize decisions to make us happy (whatev…

You may be referring to this TED talk by Dan Gilbert - http://www.ted.com/talks/lang/eng/dan_gilbert_asks_why_are_w...
Post reply on HN