On 2007.11.25., at 2:59, Charles Oliver Nutter wrote:
>
> Szegedi Attila wrote:
>> Hello all,
>>
>> I finally got around to packaging up the current state-of-art of my
>> metaobject protocol library as a downloadable release (source +
>> binaries
>> + documentation), plus putting up a very basic website (hosting an
>> information page + JavaDoc) for it.
>
> Notes as I work through the implementation.
>
> * I know we talked about this at one point, but it would fit better
> with
> JSR292 and even current hotspot optimizations if the MOP provided a
> way
> to invoke by querying for method objects. So rather than
>
> Object call(Object target, Object id, CallProtocol protocol, Map args)
>
> You'd have
>
> Callable query(Object id, Map args) (args for signature selection
> where
> appropriate)
>
> And use the resulting callable.
>
> The largest performance gains we've seen in JRuby have resulted from
> moving away from call() invocation to getMethod().invoke() invocation,
> since it brings the target method (presumably either a reflected
> method
> or in JRuby's case a code-generated stub) closer to the call site.
> Bottom line: reduce the distance between call site and target code.
> call() interface makes it at least one hop.
Sure, I'm fully open to adding this. I just wanted you to come up with
an API that's ergonomic for your use case instead of me trying to
guess the right API. It'd also be easy to provide a default
implementation for MetaObjectProtocolBase that'd just delegate to
get() by ID (that is, return a polymorphic callable), and then allow
those MOPs that know how to be more efficient to override it (i.e. the
BeansMetaobjectProtocol can clearly override it).
>
>
> * For languages that don't support keyword arguments, I think it may
> be
> better to have the call(..., Map) signature to take SortedMap instead.
> Otherwise there's no way to predictably map the arguments into an
> array.
> Right now I'm using .values().toArray(), which will not work
> predictably
> for args.length > 1.
Well, the idea is that a callable that has no support for calling with
named arguments (i.e. a plain Java method) would not be callable with
call(..., Map) method. Such method would just return noAuthority. You
shouldn't be forced to convert a named-args call into a positional-
args call in an ad-hoc manner. If your callable doesn't understand
named arguments, then it should just claim no authority.
>
>
> * I presume there's no affordance for a closure parameter on the call
> signatures because it would be just another argument (or arguments) to
> the method, yeah? Or perhaps because Java has no standard way to pass
> closures yet, so anyone using the Java API would need to implement
> their
> own mechanism anyway?
Pretty much, yeah. I'd assume a closure is a Callable with as many
arguments as it declares. Again, if you'd have more specific
requirements for closure support, feel free to articulate them.
>
>
> * I'm debating the best way to implement the property accessors. In
> Ruby, there's no distinction between properties (attributes) and
> normal
> methods. Setting an attribute is done through a foo= method, and
> getting
> is done through a foo method. I can make a property named "foo" to
> calling these methods, but that seems a little strange. However
> mapping
> it to instance variables seems even more wrong, since ivars are never
> public in Ruby unless you provide attribute accessors for them.
> Thoughts?
I think the natural way for this would be that your MOP implements
get(obj, "foo") as invocation of the "foo" method, and put(obj,
"foo", ...) as invocation of the "foo=" method.
>
>
> * There is no apparent way to represent method visibility. For the
> moment, I'm implementing it to ignore the visibility of the target
> methods.
You're right that there is no concept of method visibility or any
other language-specific access restrictions. I didn't consider
enforcing access restrictions. If you'd want to do that, you'd need to
pass an object adequately representing the call site's code domain
into the call() method. We can discuss that.
>
>
> * I'm using results as follows:
>
> - noAuthority if it's not an IRubyObject (though we want to move
> toward
> all invocations working with Object instead of IRubyObject)
That's correct.
> - noRepresentation if any parameters are not IRubyObject instance or
> for
> methods that don't map correctly like integer property access
Well, there's the expectation that your code would be called from a
different language runtime, and it might pass objects from a different
language as arguments for the call, or at the very least pass some
usual Java value-type objects (Boolean, Double, String) so I think
you'd want to be more lenient with parameters. Of course, if you are
definitely unable to handle the object, you're free to reject it at
any point, but my general vision for using the MOPs would be that code
written in dynamic language would accept pretty much any object, and
then fail as late as possible during runtime at the point where it
can't use it as it wished. I.e. if it would use it as a branch
predicate and can't obtain a representAs(Boolean.class) representation
for it from any MOPs it works with.
As for integer property access -- if your object has no integer
properties, you can either return doesNotExist, or better yet, return
noAuthority. This gives theoretical chance to some funky extension MOP
someone integrated into their runtime environment to be asked for this
in turn, and to provide such support "bolted on".
> - notDeletable for all deletes since attributes don't support the
> notion
> of deletion, only setting and getting
That's correct. If you could answer to a get() request with a value,
but you don't support deleting, then notDeletable is appropriate.
>
>
> * How would one map propertyIds to a language that doesn't support a
> separate notion of properties? There's nothing for me to list. This
> also
> applies to has().
You can then just not list anything :-) I.e. JavaScript has the syntax:
for(var propId in obj) {
...
}
that you can use for things like:
for(var propId in obj) {
print(obj[propId]);
}
But even in JS, not all properties need to be visible to enumeration.
If your language doesn't have the notion of iterating over the
available properties, you can just omit this. OTOH, if you can deduce
-- in a computationally cheap way -- to which properties would you be
able to give a definitive answer to get() and put() request, you can
just list them and also return Boolean.TRUE from has().
> * Is representAs intended to be only for coercion to normal Java
> types?
> There doesn't appear to be a good way to use this for Ruby's built-in
> coercion from one Ruby type to another since they sometimes don't have
> Java type equivalents. If it's only intended for actual Class types,
> that's probably ok, but it will have a reduced applicability.
> Implementing to return noAuthority for now.
Yes, that's something that occurred to me as well about a week ago --
how representAs() would be more generally useful if it allowed some
language specific class-like objects to represent the desired target
type in addition to Java classes.
>
>
> * Why does put(Object, Object || long, Object, CallProtocol) pass
> protocol but get does not?
The property/attribute might only be able to accept a value of certain
type. I.e. consider a POJO method "void setFoo(String s)" -- it can
only accept a String. The put() will then need to invoke
callProtocol.representAs(String.class) before it can store the value.
get() OTOH just returns the value as is; it's then up to the caller to
convert the value if it needs to.
> * Plural enums and types bug me. It should be "Result", not "Results".
Fair enough -- in my defense, that was probably the first time I used
enums (I don't do much Java 5 targeted development yet). I'll change
that in a subsequent release.
>
>
> * Are the call methods with no target intended to represent calling
> static methods? In Ruby's case, there's no distinction between
> "static"
> and "instance" methods, since all methods can only be called on an
> instance of something. The rough equivalent to static methods would be
> instance methods on the metaclass, and that's how I'm implementing
> these
> call methods.
Nope. In my view, the target of a call should never be null. The Java
static methods could be implemented by creating an object
representation of their classes, with static methods exposed as
properties of these class objects. That's something that I'd either
left to an individual language runtime (i.e. in Rhino, we have a
proprietary top-level object named "Packages", aliased to "com",
"org", "net", and "java", so that i.e.
"java.lang.System.currentTimeMillis()" works), or provide a generic
implementation such as that the BeansMetaObjectProtocol would return
instances of these classes for objects' "class" property. That's open
for discussion.
>
>
> And now I've completed the implementation for the moment. I've
> attached
> a patch to our RubyClass object that shows the implementations, and
> they're also available here:
>
> http://pastie.caboo.se/121597
>
> I'm interested in talking through the bullets above and discussing how
> to improve the MOP.
>
> - Charlie
>
> >
Attila.
--
home: http://www.szegedi.org
weblog: http://constc.blogspot.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "JVM
Languages" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/jvm-languages?hl=en
-~----------~----~----~----~------~----~------~--~---