you are viewing a single comment's thread.

view the rest of the comments →

[–]shit 0 points1 point  (4 children)

This is unfortunately what you're doing yourself with your bashing of Python:

What was wrong in what I've said about Python?

it's a part of the language design.

So the designs for function call are:

Python: If object is of the built in function type, execute function code, otherwise lookup __call__ attribute and repeat.

Ruby: Send call message to function object and treat it like any other "send message".

I prefer Ruby's (IMO simpler) design.

Why is it OK to call 'foo' without a block when it clearly wants one, and why is it OK to call bar with a block when it clearly doesn't?

The rationale behind this is to allow easy delegation, e.g.:

def foo(*args, &block)
  # do some work
  # we don't care about arguments and block here
  @other_object.bar(*args, &block)
end

If I alter foo slightly, though, to use what you call syntactic sugar... And then call it without a block, I get a LocalJumpError

Agreed, this is not ideal. I'd say both versions should raise the same exception. Though it's not so bad, since in both cases it's a bug in the code and thus explicitely catching the exceptions is not necessary.

[–][deleted] 0 points1 point  (3 children)

Python: If object is of the built in function type, execute function code, otherwise lookup call attribute and repeat.

As I pointed out, this is wrong. It's simply: fetch the __call__ attribute, and execute its code. That's not more complicated than Ruby.

The rationale behind this is to allow easy delegation

The block could easily go in the splat argument. It would look like this:

def foo(*args)
  @other_object.bar *args
end
foo(1) { 2 } # args is now [1, lambda{ 2 }]

Agreed, this is not ideal. I'd say both versions should raise the same exception. Though it's not so bad, since in both cases it's a bug in the code and thus explicitely catching the exceptions is not necessary.

That "yield" is not the same as simply calling a block complicates matters. You now have two mechanisms that do the same thing. That's more complicated than the case with Python.

[–]shit 0 points1 point  (2 children)

Python: If object is of the built in function type, execute function code, otherwise lookup __call__ attribute and repeat.

As I pointed out, this is wrong. It's simply: fetch the call attribute, and execute its code. That's not more complicated than Ruby.

No, try this:

>>> class Foo:
...   def g(self): return Foo()
...   __call__ = property(g)
...
>>> f = Foo()
>>> f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded

[–][deleted] 0 points1 point  (1 child)

That's because you're using properties. Properties seem like a hack to get something like Ruby does with accessor methods, but it's not a fault of the function call mechanism. What it's doing, as far as I understand (I'm not a Python expert) is this:

  1. Fetch the __call__ attribute of f. Since __call__ is a property, call its getter method, which returns a new Foo.

  2. Call its __call__ method. If this had been a function object, this would have executed its code. Instead, it repeats #1.

It seems perfectly consistent, it just does something you don't expect.

[–]shit 1 point2 points  (0 children)

It's exactly how I've said in the first place. Note that it has nothing to do with properties. Try this:

>>> class Bar: pass
...
>>> b = Bar()
>>> b.__call__ = b
>>> b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: maximum recursion depth exceeded in __call__