Sure, I understand the idea of GIL being faster than fine-grained locking for single-threaded apps. This is not how I read the article though (maybe that's what they meant...).
The speed can be sorted as (in most cases): single-threaded code, single-threaded with GIL, single-threaded with fine locks.
"Python is hurting from a bad design decision that doesn't let multiple interpreter implementations share components that they could have."
As many others have said, changing syntax is really one of the most trivial parts for alternative implementations to keep up with when moving to new versions. That "hurt" hasn't been fixed because it doesn't really hurt very much...
It's not hard if we disallow statements, and make everything into an expression that returns a value -- something that Ruby does right. The value of a block of statements is just the value of the last statement in the block.
So, whether you call it a function, or a method, or a lambda, or a block, whitespace with multi-line expressions can be made to work.
The commonly-cited lambda-with-multiple-prints example, in CoffeeScript:
Wow, I've never seen that before. I'm also not sure I'd say a solution that uses bytecode rewriting is really working within the language, but that's a pretty interesting package anyhow.
Well, normally I would agree - but if its entirely abstracted away in a library (and the library is well written) then it might sort-of-kind-of-maybe be ok.
Python itself does varying degrees of bytecode optimisation depending on which version you're using.
I fail to see how that is flame bait. It's actually a quite insightful statement that is worth repeating: Python, Ruby and all other languages exist for a reason. The reason is that other languages, including Lisp, are not considered to be equally useful for the tasks for which a specific language is being used. Python is not only not Lisp: it's not Java, not Haskell, not Erlang, not ...
Whether it is a good reason, and whether these considerations are sensible, is a separate discussion. Whichever way you put it: they exist and should be acknowledged.
I agree. Lisp just plain looks weird because of all the parentheses and the prefix notation. I also can't help get the feeling that even with macros available to me, coding directly in the form of parse trees just might not be optimal. Also, it makes my head hurt sometimes.
As I sit here writing this, it strikes me that someone who's using these kinds of things to avoid learning Lisp is a lot like the Python newbie who's discovered the language and wanders on to comp.lang.python complaining about the syntactically significant indentation. As with most matters, I'm inclined to believe that Lisp's problem here is primarily a people problem rather than a technical problem.
Oh, come on. You're just disagreeing for effect here.
I've never used Ruby but that snippet is clear as day.
The hell it is. I find it quite confusing. I'll take the Python 2 solution anyday, which clearly seperates what is called from when it is called. One of the main problems with the 'my language is so flexible' approach is that inventing concise solutions becomes a goal in itself.
> which clearly seperates what is called from when it is called
You're passing some code as an argument to a function. Many other languages do this. Do you hate Lisp and Haskell, too? Do you not use continuations in Python? Or callback functions?
If you pass a function as an argument to a function, you're still separating what from when: the function declaration shows what; the invocation of the function shows when. It doesn't matter whether it's
def foo(f):
f()
or
(define (foo f) (f))
That has nothing to do with annotating a function to show it binds to some URL and is invoked when an HTTP GET request is received with that URL. I don't like having to scroll through a source file to discover such bindings and I actually prefer them to be defined at a separate place. In that sense, the Python 2 solution isn't good either and suffers from the same problem I described earlier: people try to be so concise that they loose sight of other goals.
The Ruby code is not an annotation. It's very close to passing an anonymous function to a function. The definition of get is
def get(path, opts={}, &block)
See that &block? Here's the actual source of get:
# Defining a `GET` handler also automatically defines
# a `HEAD` handler.
def get(path, opts={}, &block)
conditions = @conditions.dup
route('GET', path, opts, &block)
@conditions = conditions
route('HEAD', path, opts, &block)
end
Think of it as "I'm registering this block as a callback, to be called when this url is processed." I'm not sure how else you'd like to have a web DSL written; every web library I've used has worked this way.
Which is exactly the problem: it should be, because it is meta-information about when the function is supposed to be called. It's not a good idea to wrap that into a function, together with the code that is supposed to be executed, because you are mixing two completely different kinds of information into some new function. This code is being too clever for it's own good: it condenses information so far that you are forced to think harder about what each piece means. I consider this is one of the main dangers of languages like Ruby: it's proponents lose sight of the fact that they are coming full circle, back to writing code that's so clever that no one in the future will understand it.
every web library I've used has worked this way.
I doubt that we've used a disjoint set of web libraries, so my only answer can be: no, they haven't worked that way. Most libraries map paths to named functions separately from implementing the functions.
Conciseness is not an ultimate goal; in fact, it is rather the opposite, as those that suffered under the clever C hacks of others will tell you. To paraphrase Santanyana: people have forgotten the past and are repeating it.
I guess we'll just have to agree to disagree on this. By keeping the function definition near the route instead of just defining a name, I feel that it's much, much cleaner.
> I doubt that we've used a disjoint set of web libraries,
I guess I mis-spoke slightly. I meant assigning a function to a route, because I consider that (in this case) it's an anonymous function to be an irrelevant detail. Basically, to me
get "/hello", hello
def hello
puts "hello"
end
to be the as clear or less clear than the way Sinatra does it. Rails and Zend (the two frameworks I've used the most) put that 'def hello' in another file, elsewhere.
Anyway, thanks for the discussion. We've apparently boiled it down to taste, so it's a good thing there are multiple languages, I guess.
Thanks to you as well :). I even may come to see it your way in the near future, as it seems I'm going to be doing some Ruby coding for my new employer.
> Oh, come on. You're just disagreeing for effect here.
It's unclear what "get" is. Is it a function? What does it return? My guess would be that it returns a function bound to the handler of a "/hi" url, but that's a guess.
It's possible that you just haven't read enough ruby, then. There are only two variations on the syntax of passing a block to a function, "do/end" and "{ }". By convention, multiline blocks use do/end, and single line blocks use {}.
It's also possible that one of the things in the article is the gotcha, flexible parenthesis. By convention, Rubyists tend to use parenthesis when defining functions, but not when calling them:
def get(path, opts={}, &block)
get "/hello" do
not
get("hello") do
Which would still work, if you preferred. The exception to this is chaining method calls:
I'm pretty sure Python picked it up after Java did. But, I don't think it's a case of imitation; it's just that the glyph '@' happened not to have syntactic meaning attached to it and it looks okay.