Attila Szegedi wrote:
> 
> On 2007.11.25., at 2:59, Charles Oliver Nutter wrote:
>> * 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
...
> 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).

Ok, I can do that.

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

That's entirely fair. Can you describe for me a bit better the typical 
result of a noAuthority and how it would be used by a client calling 
into the MOP? Is this something where the client would revert to an 
ordered param list in response and re-attempt the call?

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

That largely matches with what I expected. I hadn't started thinking 
about the representation of closures in this model, though. I'm not sure 
it's necessary; as in Groovy, they can just be callable objects.

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

That's how I've done it. I'm still not convinced either way, but this 
does seem to be the best mapping.

>> * 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've been debating with myself whether visibility is a factor of the 
calling class, the calling object, or the calling method. I believe the 
JVM enforces it based on the calling class exclusively, but Ruby for 
example has other mechanisms for checking/enforcing visibility. For 
example, private methods:

- can be invoked with a functional form as in foo()
- can be invoked with a variable form as in foo
- can not be invoked with a qualified form that has an explicit 
receiver, as in self.foo(), even from within the same class

So JRuby defines a CallType enum with three values NORMAL, FUNCTIONAL, 
and VARIABLE. Protected calls have similar restrictions but an 
affordance for the class and subclasses to use the qualified form.

Since other languages don't have such a concept, I will have to think 
more on how this would be represented. Visibility isn't as simple in all 
languages as "can type A call method X on type B?".

>> * I'm using results as follows:
...
>> - 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.

Ok, I understand. And it's clearer now how representAs should work; 
we're using Java types as least-common-denominator. What would you think 
of providing a set of least-common-denominator interface types instead? 
representAs(MetaString.class) to result in a "meta string" type that can 
encapsulate any language's rudimentary string type, with similar 
interfaces for primitive lists, hashes, and so on. A wider vocabulary of 
common types, but a high-level abstraction than simple unextendable, 
unimplementable Java types. And likely more overhead, but potentially 
worth it?

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

Ahh, now the MOP delegation logic is starting to come clear. So in 
JRuby, for example, the idea is that there would be two MOPs installed: 
one for JRuby's types, and a fallback MOP using Java types and "beans" 
logic. So if the object in question is a Ruby object type, the main 
JRuby MOP would have authority. If it is not a Ruby object type, the 
next MOP in the chain is invoked, falling back on java/bean dispatch.

In an environment with both JRuby and Groovy, the JRuby MOP may be 
primary authority (since in its current form it's the more restrictive) 
with the Groovy MOP coming second. And ideally the Groovy MOP would fall 
back on "standard" java/bean dispatch at some point.

...
>> * 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().

How about read-only or write-only properties? There's no affordance for 
determining whether a property is readable or writable; only if it 
exists. For hasReadable() I would look for a method of the same name, 
since that's the best I can do for property reads. More useful would be 
hasWritable() which would look for the '=' form. As it stands, the 
safest computation I could do to determine if there's properties would 
be to look for assignable attributes only. I may implement it that way.

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

And again consider my thought above about providing a set of base type 
descriptors in this MOP library that all languages could use as a 
contractual base type agreement. It would vastly improve the utility of 
passing a "string" from language A to language B if both languages could 
see the same object as a "string-like thing". Similar for arrays/lists, 
hashes/maps, numeric types...and of course, metaclasses. I expect 
there's a small finite set of types we'd find it useful for language 
implementations to share in common, and anything more complex would be 
compositions of those types or user-defined.

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

Ok, I have a better understanding of CallProtocol interface now as well. 
It's intended to be a utility interface provided to the MOP for things 
like type conversions. So if I pass a JRuby object to some arbitrary 
MOP, I would also pass in a CallProtocol implementation that knows how 
to perform conversions. I'll give this more thought and get back to you.

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

Excellent. It just reads nicer. In your defense, I typically used 
plurals for interfaces that contained static final values...but as we've 
migrated to enums we've singularized those types.

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

That maps well with how Ruby represents static methods. I think it's 
probably reasonable to just have the beans MOP be able to provide a 
metaclass for a given Java class, to be used for static invocations. And 
I agree, I feel better about invoking a method against *something*.

On the other hand, I'm also approaching this with an eye for 
optimization; requiring that static invocations go through a MOP 
invocation against a class-representing object could make it harder to 
dynamically optimize code in a language runtime or in the JVM itself. Hmmm.

Somewhat OT, but we do the same thing in JRuby, but only for com, org, 
java, and javax (net seemed to be fairly rare and also much more likely 
to collide with a real method name), and they're represented as methods 
(java.lang.System calls the System method on the result of calling the 
lang method on the result of calling the java method; java and lang 
return Package objects and System returns a class).

- Charlie

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

Reply via email to