Hacker Newsnew | comments | ask | jobs | submitlogin
5 points by rbanffy 176 days ago | link | parent

"Without powerful blocks and lambdas, you can’t have Rake. You can’t have RSpec. You can’t have Sinatra’s clear REST syntax."

Can anyone explain why?



10 points by mcav 176 days ago | link

Disagree on "REST syntax". Python could do just as well:

Sinatra:

  get '/hi' do
    "Hello World!"
  end
Python 2:

  @get('/hi')
  def hello():
    return "Hello World"
Python 3:

  def hello() -> "/hi":
    return "Hello World"

-----

7 points by steveklabnik 176 days ago | link

A Rubyist would argue that that Python 2 syntax is anything but clear.

I have no idea what that Python 3 snippet is doing... care to share? That looks like something that's hard to Google...

-----

13 points by fuzzyman 176 days ago | link

As a Pythonista I have no idea what the Ruby snippet is doing. Readability depends entirely on context.

The Python 3 version is using a function annotation.

In Python 2 or 3 you could use a context manager (with statement) to similar effect.

-----

23 points by aerique 176 days ago | link

As a Pythonista I have no idea what the Ruby snippet is doing. Readability depends entirely on context.

Oh, come on. You're just disagreeing for effect here.

I've never used Ruby but that snippet is clear as day.

The Python 3 snippet suggests to me that the output would be "/hi" though :)

-----

6 points by Confusion 176 days ago | link

  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.

-----

4 points by steveklabnik 176 days ago | link

> 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?

-----

1 point by Confusion 175 days ago | link

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.

-----

2 points by steveklabnik 175 days ago | link

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.

-----

2 points by Confusion 175 days ago | link

The Ruby code is not an annotation.

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.

-----

2 points by steveklabnik 175 days ago | link

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.

-----

2 points by Confusion 175 days ago | link

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.

-----

4 points by rbanffy 176 days ago | link

> 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.

-----

1 point by steveklabnik 176 days ago | link

"get" is a function that takes a string and a block.

And the block is bound to the uri given in the string, so your guess is correct.

-----

2 points by rbanffy 176 days ago | link

Ruby always confuses me with this flexible syntax...

-----

3 points by steveklabnik 176 days ago | link

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:

    Users.find_all_by_age(23).select {|user| user.first_name == "steve" }
... not that that's an awesome use of ActiveRecord, but whatever.

-----

4 points by steveklabnik 176 days ago | link

> As a Pythonista I have no idea what the Ruby snippet is doing.

Fair enough. Arguing over beauty is silly anyway, I just wanted to point out that the entire thing is entirely subjective.

> The Python 3 version is using a function annotation.

Thanks, checking out PEP 3107 now. I've always seen annotations done with the @annotation syntax like the Python 2 snippet has.

-----

5 points by algorias 176 days ago | link

> Thanks, checking out PEP 3107 now. I've always seen annotations done with the @annotation syntax like the Python 2 snippet has.

The @ syntax is for decorators. Different beast altogether.

-----

1 point by blasdel 176 days ago | link

No, they're exactly the same beast!

I've used decorators to add attributes to function objects in several projects in the past -- you don't have to wrap the original.

-----

2 points by steveklabnik 176 days ago | link

Ah. So, Python uses the Java annotation syntax for decorators? Hm. One more thing I'll have to look into when I get the time.

-----

3 points by rbanffy 176 days ago | link

Actually, I think Java uses Python decoration syntax for annotations, but I have been wrong before.

-----

1 point by pmiller2 176 days ago | link

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.

-----

2 points by mcav 176 days ago | link

It's using function annotations as a way to describe which URL to route to. Function annotations aren't used by anything in Python3 by default, so it's one possible way to do that.

-----

1 point by rbanffy 176 days ago | link

I would prefer to use a decorator for that.

-----

1 point by chromatic 176 days ago | link

> A Rubyist would argue that that Python 2 syntax is anything but clear.

Why does that matter? The syntax should be clear to a capable Python developer, not someone who has never used Python.

I'm not suggesting that that particular syntax is or is not clear to said Python programmer, merely that the consideration for a Ruby programmer is useless.

-----

1 point by steveklabnik 176 days ago | link

Because "he's a Rubyist" is the reason he would say something like "you can't have Sinatra's clear syntax."

-----

5 points by jashkenas 176 days ago | link

Just for kicks. In CoffeeScript:

    get '/hi', ->
      "Hello World"
You can have your cake and eat it too.

-----

3 points by showell 176 days ago | link

Decorators and annotations are very useful in Python, but I still like the terseness of Ruby--no need to define "hello()". Of course, Python wins on a different kind of terseness--no do/end--but that is mostly orthogonal.

-----

1 point by steveklabnik 176 days ago | link

And you could use {} instead of do/end, but a Pythonista wouldn't consider that any more terse.

-----

1 point by showell 175 days ago | link

Yep, my mention of do/end or squigglies was just a caveat on the Ruby-is-more-terse-in-this-example statement in the context of anonymous functions or blocks. I wish there was a language that won on both terseness metrics--i.e. a Ruby with indentation or a Python with anonymous functions.

-----

2 points by unbracketed 176 days ago | link

I've never worried much about which language I use to GET hi. As long as the end result is same, everyone should be feeling good.

-----

5 points by arockwell 176 days ago | link

Python doesn't have multiline lambdas, so there is no way to create an api similar to rake's:

  task :name do |t|
   # do stuff
  end
I'm not sure what's the closest equivalent syntax you could create in python.

-----

15 points by tsuraan 176 days ago | link

Python's answer to blocks tends to be the with statement, so your example would look like

  with task('name') as t:
    # do stuff with t
It's not really the same; the task object doesn't have control over whether the block is actually run, but sometimes it is sufficient.

-----

5 points by fuzzyman 176 days ago | link

You could almost certainly achieve that with the implementation of blocks provided by this package:

http://pypi.python.org/pypi/withhacks/

-----

4 points by tsuraan 176 days ago | link

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.

-----

2 points by fuzzyman 176 days ago | link

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.

-----

1 point by bad_user 176 days ago | link

that's cool, thanks ... I was wondering about that

-----

10 points by tghw 176 days ago | link

Why do you need lambdas to do this? Why wouldn't an inline function work?

-----

4 points by grandalf 176 days ago | link

This is correct, it would work. People who harp on this probably don't actually use Python for anything.

-----

3 points by viraptor 176 days ago | link

Not sure what this snippet does, but if it creates a block that is registered as a task named "name", then you can do the same with decorators:

    def task(f):
       register_task(f.__name__, f)
       return f
    
    @task
    def name(t)
       .....
or similar.

-----

3 points by algorias 176 days ago | link

Exactly. I fail to understand why people are so dismissive of languages/approaches that do things slightly differently and don't implement equivalent things in the exact same way. What's so special about the rake syntax?

-----

1 point by jashkenas 176 days ago | link

Another CoffeeScript snippet, if you're working with Node.js or the like:

    task 'name', (t) ->
      # do multiline stuff

-----

1 point by steveklabnik 176 days ago | link

For something like RSpec, you can't monkey-patch in Object#should.

-----

1 point by tghw 176 days ago | link

Are you saying this because you can't monkey-patch literals? Because you can very much monkey-patch everything else in Python.

-----

1 point by steveklabnik 176 days ago | link

As far as I know, Python doesn't have open classes, right?

-----

1 point by tghw 176 days ago | link

  >>> def print_a(inst):
  ... 	print inst.a
  ... 	
  >>> class Foo(object):
  ... 	def __init__(self, a):
  ... 		self.a = a
  ... 		
  >>> Foo.print_my_a = print_a
  >>> f = Foo(5)
  >>> f.print_my_a()
  5
Not sure why my previous post was downvoted, unless maybe your definition of monkey-patching is different than mine.

-----

4 points by steveklabnik 176 days ago | link

Well, I didn't downvote it, but you can't do this with _everything_ in Python, like you can in Ruby.

    $ irb
    irb(main):001:0> class Numeric
    irb(main):002:1>   def wtf
    irb(main):003:2>     puts "hey"
    irb(main):004:2>   end
    irb(main):005:1> end
    => nil
    irb(main):006:0> 5.wtf
    hey
    => nil

-----




Lists | RSS | Search | Bookmarklet | Guidelines | FAQ | News News | Feature Requests | Y Combinator | Apply | Library

Analytics by Mixpanel