A proposal for attribute lookup failures

M

MonkeeSage

Proposal:

When an attribute lookup fails for an object, check the top-level
(and local scope?) for a corresponding function or attribute and apply
it as the called attribute if found, drop through to the exception
otherwise. This is just syntactic sugar.


Example:

a = [1,2,3]

a.len()
# -> fails,
# -> finds len() in the top-level symbol table,
# -> applies len(a)
# -> 3

a.foobar()
# -> fails,
# -> no foobar() in scope,
# -> raise NameError


Benefits:

- Uniform OO style. Top-levels can be hidden as attributes of data.
Most of the top-level functions / constructors can be considered as
attributes of the data; e.g., an int() representation of a string can
be considered as _part_ of the semantics of the string (i.e., one
_meaning_ of the string is an int representation); but doing it this
way saves from storing the int (etc) data as part of the actual
object. The trade-off is speed for space.

- Ability to "add" attributes to built-in types (which is requested
all the time!!) without having to sub-class a built-in type and
initialize all instances as the sub-class. E.g., one can simply define
flub() in the top-level (local?) namespace, and then use "blah".flub()
as if the built-in str class provided flub().

- Backwards compatible; one can use the top-level functions when
desired. No change to existing code required.

- Seemingly trivial to implement (though I don't know much C). On
attribute lookup failure, simply iterate the symbol table looking for
a match, otherwise raise the exception (like current implementation).


Drawbacks:

- Could hide the fact that an extra (On?) lookup on the symbol table
is necessary for attribute lookup failure. (Maybe there could be a
switch/pragma to enable (or disable) the functionality?)

- As above, attribute lookup failure requires an extra lookup on the
symbol table, when normally it would fall through directly to
exception.

- ???


Disclaimer:

I realize that very often what seems good to me, ends up being half-
assed, backwards and generally bad. So I'd appreciate input on this
proposition. Don't take it that I think the idea is wonderful and am
trying to push it. I am just throwing it out there to see what may
become of it.

Regards,
Jordan
 
J

James Stroud

MonkeeSage said:
Proposal:

When an attribute lookup fails for an object, check the top-level
(and local scope?) for a corresponding function or attribute and apply
it as the called attribute if found, drop through to the exception
otherwise. This is just syntactic sugar.


Example:

a = [1,2,3]

a.len()
# -> fails,
# -> finds len() in the top-level symbol table,
# -> applies len(a)
# -> 3

a.foobar()
# -> fails,
# -> no foobar() in scope,
# -> raise NameError


Benefits:

- Uniform OO style. Top-levels can be hidden as attributes of data.
Most of the top-level functions / constructors can be considered as
attributes of the data; e.g., an int() representation of a string can
be considered as _part_ of the semantics of the string (i.e., one
_meaning_ of the string is an int representation); but doing it this
way saves from storing the int (etc) data as part of the actual
object. The trade-off is speed for space.

- Ability to "add" attributes to built-in types (which is requested
all the time!!) without having to sub-class a built-in type and
initialize all instances as the sub-class. E.g., one can simply define
flub() in the top-level (local?) namespace, and then use "blah".flub()
as if the built-in str class provided flub().

- Backwards compatible; one can use the top-level functions when
desired. No change to existing code required.

- Seemingly trivial to implement (though I don't know much C). On
attribute lookup failure, simply iterate the symbol table looking for
a match, otherwise raise the exception (like current implementation).


Drawbacks:

- Could hide the fact that an extra (On?) lookup on the symbol table
is necessary for attribute lookup failure. (Maybe there could be a
switch/pragma to enable (or disable) the functionality?)

- As above, attribute lookup failure requires an extra lookup on the
symbol table, when normally it would fall through directly to
exception.

- ???


Disclaimer:

I realize that very often what seems good to me, ends up being half-
assed, backwards and generally bad. So I'd appreciate input on this
proposition. Don't take it that I think the idea is wonderful and am
trying to push it. I am just throwing it out there to see what may
become of it.

It would be unoriginal of me to suggest that this violates the explicit
is better than implicit maxim. But it does.

Also, you have picked the perfect use case for a counter argument:

py> class NoLen(object):
.... pass
....
py> len(NoLen)
------------------------------------------------------------
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
<type 'exceptions.TypeError'>: object of type 'type' has no len()

So this proposal would send the interpreter through two cycles of trying
to find the proper attribute before it failed.

Plus, I probably haven't even raised the best arguments against it, but
my feeling is that it has serious problems and is better left out of the
language.

But it is an interesting idea nonetheless.

James


--
James Stroud
UCLA-DOE Institute for Genomics and Proteomics
Box 951570
Los Angeles, CA 90095

http://www.jamesstroud.com
 
A

Arnaud Delobelle

Proposal:

When an attribute lookup fails for an object, check the top-level
(and local scope?) for a corresponding function or attribute and apply
it as the called attribute if found, drop through to the exception
otherwise. This is just syntactic sugar.
[...]
Benefits:

[...]

- Backwards compatible; one can use the top-level functions when
desired. No change to existing code required.

It changes how the following code executes:

--------------------------
def foo(x): return x.foo()
foo(1)
--------------------------

* currently this raises AttributeError
* under your proposal this code would fill the stack and raise a
RuntimeError I guess.
- Seemingly trivial to implement (though I don't know much C). On
attribute lookup failure, simply iterate the symbol table looking for
a match, otherwise raise the exception (like current implementation).

This is not a benefit!
 
M

MonkeeSage

It would be unoriginal of me to suggest that this violates the explicit
is better than implicit maxim. But it does.

That's what I meant about hiding the complexity of an attribute
failure. Though, sometimes implicit is acceptable (e.g., promotion of
int to float by __add__ when RHS is a float). Perhaps not here though.

Also, you have picked the perfect use case for a counter argument:

py> class NoLen(object):
... pass
...
py> len(NoLen)
------------------------------------------------------------
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
<type 'exceptions.TypeError'>: object of type 'type' has no len()

So this proposal would send the interpreter through two cycles of trying
to find the proper attribute before it failed.

Ah. Good point. Hadn't thought of that.

--------

It changes how the following code executes:

--------------------------
def foo(x): return x.foo()
foo(1)
--------------------------

* currently this raises AttributeError
* under your proposal this code would fill the stack and raise a
RuntimeError I guess.

I think it would be easy enough to detect and avoid cyclic references
like that (it's done in other instances in the language). An exception
could be thrown in such a case. But maybe it would be more difficult
than I imagine, leading to even further complexity (bad).

I think that, given that it hides a complex operation (which could
easily lead to abuse by the "unwashed masses"), and that in many cases
multiple lookups in the symbol table would be necessary before
termination (as in James' example above), as well as having to deal
with cases such as you mention, perhaps it's not such a good idea.

Off the cuff, it seemed like a nice feature (and still does, if it
could be implemented without the drawbacks). But such is life. :)

Regards,
Jordan
 
M

MonkeeSage

Ps. Just for kicks, here is a simple ruby 1.8 mock-up of the proposal
(sorry for using ruby, but I don't know enough C to start hacking the
CPython backend; I think that a higher-level example is conceptually
clearer anyhow). Reference cycles are not detected in the example.

#!/usr/bin/ruby

class Object
def method_missing(fun, *args)
begin
TOPLEVEL_BINDING.method(fun).call(self, *args)
rescue
raise(NoMethodError,
%{undefined method `#{fun.to_s}' for "#{self}":String},
[])
end
end
end

def myfun(s1, s2)
s1 + s2
end

puts "foo".myfun("bar")
# -> foobar

puts 1.myfun(2)
# -> 3

puts "foo".nofun("baz")
# -> ./attr_fail.rb:10: undefined method `nofun' for "foo":String
(NoMethodError)
 
K

Kay Schluehr

Ps. Just for kicks, here is a simple ruby 1.8 mock-up of the proposal
(sorry for using ruby, but I don't know enough C to start hacking the
CPython backend; I think that a higher-level example is conceptually
clearer anyhow).

No need to excuse. I think Ruby provides a nice context for discussing
the semantics of top level "open classes". But I think those are
entirely different than your contextual bindings. Note I find your
proposal somewhat confusing since I expect that an attribute is
"owned" by an object and is not an arbitrary contextual property.

Regarding open classes in Python, what about "extension classes"?

class +object:
def len(self):
return self.__len__()

This either adds the len method to the namespace of object or it
creates a new namespace which is associated with the namespace of
object s.t. each object can lookup attributes in this namespace when
default lookup fails. I'm not entirely sure about scope. I think the
lifetime of an extension class shall be determined by the lifetime of
the extended class and not by the scope in which the extension class
is defined. What do you think?

Kay

PS. you can use EasyExtend when you want to provide a language
extension without hacking the CPython runtime. EE was made for such
kinds of experiments.
 
M

MonkeeSage

No need to excuse. I think Ruby provides a nice context for discussing
the semantics of top level "open classes". But I think those are
entirely different than your contextual bindings. Note I find your
proposal somewhat confusing since I expect that an attribute is
"owned" by an object and is not an arbitrary contextual property.

I obviously like ruby as well as python, I just didn't want to turn
the discussion into a "I can do this in ruby, can you do that in
python" thread, so my apology was more along those lines that an
apology for using ruby at all. :)

As far as "ownership" of an object, that's what object abstraction is
all about is it not? Multiple instances can "own" a method via
polymorphic substitution, without a strict interface definition. My
proposal simply adds a new scope within which to look for "ownership".
Of course, there are problems with that, as noted above.
Regarding open classes in Python, what about "extension classes"?

class +object:
def len(self):
return self.__len__()

This either adds the len method to the namespace of object or it
creates a new namespace which is associated with the namespace of
object s.t. each object can lookup attributes in this namespace when
default lookup fails. I'm not entirely sure about scope. I think the
lifetime of an extension class shall be determined by the lifetime of
the extended class and not by the scope in which the extension class
is defined. What do you think?

Sounds like a interesting approach! Worth looking into, imo.
PS. you can use EasyExtend when you want to provide a language
extension without hacking the CPython runtime. EE was made for such
kinds of experiments.

Hadn't seen that before, thanks for the reference. :)

Regards,
Jordan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top