Sam Ruby wrote:
[ a lot - I'll split answers ]
Leopold Toetsch wrote:
Sam Ruby wrote:
It seems obvious, but it leads to surprises. Example:
'1' + '2'
The result will depend on what the actual types are of the inputs
perhaps even what the context is of the caller.
Ehem, given that and ... (from below)
> Note: no MMD.
> and VTABLE_add()
it seems that you are still missing the power of MMD. Just because the
result and semantics of the "add" operation are different, we are doing MMD.
The MMD dispatch looks at the left and right types of the infix
operation, locates a function that matches and call the function.
You can look at it like a matrix:
PyString PyInt PyNum Integer ...
----------------------------------------------------------
PyString py_add_s add_err add_err add_err
PyInt add_err add_i add_n add_i
PyNum add_err add_n add_n add_i
Integer add_err add_i add_n add_i
...
There are four incarnation of the "add" multi sub:
py_add_s := concat (function name Parrot_PyString_add_PyString)
add_err := emit a "TypeError : cannot concat ..."
add_i := integer add (Parrot_Integer_add_Integer)
add_n := float add (Parrot_Float_add_Float)
So what actually need to exist in Python scalar classes is a constructor
and:
in pystring.pmc
pmclass PyString extends PyObject extends String {
METHOD PMC* add(PMC *right) {
MMD_PyString: { ... do a concat }
MMD_DEFAULT: { ... emit TypeError }
}
}
in pyint.pmc (and pyfloat analog)
pmclass PyInt extends PyObject extends Integer {
// inherit add
}
The METHOD specifier shall install all given variants with
VTABLE_add_method, i.e. register the NCI functions so that
VTABLE_find_method can locate the entry.
But the matrix implementation doesn't work, because it's too static,
inheritance can't be described easily (or not at all) in terms of it.
As my proposal is primarily focused on where the logic is placed
And that's the main problem of your approach. You are forgetting MMD.
inline op add (out PMC, in PMC, in PMC) :base_core {
$1 = VTABLE_add(interp, $2, $3);
}
With that you are already calling an "add" function that depends on the
left type - this is not MMD and it forces the left operand to handle
all possible cases of add/concat with int/float/string ... This is
exactly the point, where interoperbility is violated. The left type
doesn't know all possible right types to deal with them correctly.
With that you get a cascade of "if"s that handle the different types. We
had that earlier. It was discarded in favor of MMD.
pmclass default abstract noinit {
PMC* add (PMC* left, PMC* right) {
PRESERVE_CONTEXT;
add_func = mmd_find("__add"...) // via VTABLE_find_method
and here it becomes horribly slow and weird. You are already in one
"add". What do you want to find here?
REG_PMC(5) = left;
REG_PMC(6) = right;
VTABLE_invoke(interp, add_func, 0);
and redispatching - no and no - sorry.
I know that Leo keeps telling me that I need to map __add__ to __add at
"compile time", but there are tests like t/pie/b3.t that indicate that
such tests need to be made at runtime.
When I say, that "__add__" maps to Parrot's "__add" at compile time, it
doesn't preclude that you have to do something at runtime too. E.g. you
emit code for:
foo.__add__ = myadd (or __dict__.__add__)
then the user is installing an overridden version of the "add" method.
You have to know that, or you can't do the right thing when it comes to
foo + x
So you have to call
VTABLE_add_method(class_of_foo, "__add", myadd).
If you don't do that, VTABLE_find_method(... "__add") will fail later or
return the wrong function.
But and that's the big difference of our porposals: nothing more has to
be done. The MMD dispatch at the runloop (and not inside every class)
handles the selection of the correct subroutine, being it a C function
(wrapped into NCI) or PASM/PIR user code. This just doesn't matter and a
class doesn't need to know about that.
Allowing me to subclass, extend, and replace how methods like add works
gives me a place to insert any logic I find necessary.
Which logic would you insert in add(PyInt, PyInt)? What for?
... And, when I find
out that something is not as necessary as I once thought, it can be
removed just as easily.
No because you are duplicating the method dispatch. You can't remove
that. And that's the big problem with your approach - besides that it'll
be around a factor 10 slower then the dispatch at the opcode level like
now. We can't afford a factor ten in speed decrease.
Arguements can be made against each individual example (perhaps I could
create a small function that raises Type Error and register it dozens of
times to handle the PyString.add example, and yes,
Instead of the check (if right.type isnt a PyString) you are already in
the MMD method Py_String_add (_default) - "add_err" above - no check
needed. That's MMD.
... I could make every
assignment to any Python property invalidate some portion of the method
cache,
You have to call add_method anyway, if a Sub is attached to some
attribute (with special names). And add_method is the place where the
cache for that class is invalidated.
...but that still wouldn't handle cases where two objects were of
the same class but respond differently to find_method).
You are calling VTABLE_find_method(interp, object, ...) This is exactly
the place to look at that object's properties, then you call find_method
on the class ...
- Sam Ruby
leo
|