"JVM Dynamic Languages Metaobject Protocol" now released

4 views
Skip to first unread message

Szegedi Attila

unread,
Nov 14, 2007, 5:12:36 PM11/14/07
to jvm-la...@googlegroups.com
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. I haven't got around to setting up dynalang.org yet, so the website is for now hosted at <http://dynalang.sourceforge.net>. The fact there is now a release does not intend to confer either a sense of completeness or rigidity. It is versioned at humble 0.3. It is pretty much open to modifications and is also probably not complete yet (i.e. I fully expect people to need further features for integrating with their particular language runtime). The release just strives to make it easier for people to get started with it, as it's now available as a HTTP download instead of only through SVN. Also, having a release means there's now a baseline for purposes of tracking changes in a changelog file etc. Unit tests cover about 75% of the code right now, so it's fairly safe to say it does what it is intended to do, but of course, bugs are always to be expected.
In completely unrelated news, today's also my birthday :-)

Cheers,
Attila.


David Pollak

unread,
Nov 14, 2007, 5:25:49 PM11/14/07
to jvm-la...@googlegroups.com


On Nov 14, 2007 2:12 PM, Szegedi Attila <szeg...@gmail.com> wrote:
Hello all,


In completely unrelated news, today's also my birthday :-)

Happy Birthday!
 

Cheers,
Attila.







--
lift, the secure, simple, powerful web framework
http://liftweb.net

Geert Bevin

unread,
Nov 14, 2007, 6:14:36 PM11/14/07
to jvm-la...@googlegroups.com
Happy birthday!

On 14 Nov 2007, at 23:12, Szegedi Attila wrote:

> In completely unrelated news, today's also my birthday :-)

--
Geert Bevin
Terracotta - http://www.terracotta.org
Uwyn "Use what you need" - http://uwyn.com
RIFE Java application framework - http://rifers.org
Music and words - http://gbevin.com

Ted Neward

unread,
Nov 15, 2007, 12:53:02 AM11/15/07
to jvm-la...@googlegroups.com

So, is it in a usable state?

 

Oh, yeah, and happy birthday!

 

But seriously, is dynalang ready for exploration?

 

;-)

 

Ted Neward

Java, .NET, XML Services

Consulting, Teaching, Speaking, Writing

http://www.tedneward.com


 

No virus found in this incoming message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.31/1130 - Release Date: 11/14/2007 9:27 AM


No virus found in this outgoing message.
Checked by AVG Free Edition.
Version: 7.5.503 / Virus Database: 269.15.31/1130 - Release Date: 11/14/2007 9:27 AM

Szegedi Attila

unread,
Nov 15, 2007, 4:18:51 AM11/15/07
to jvm-la...@googlegroups.com

On 2007.11.15., at 6:53, Ted Neward wrote:

> So, is it in a usable state?

It is. That's a funny story actually. I uploaded to SourceForge a
version numbered 0.2 on Tuesday. Then after I did it (but fortunately
before I announced it anywhere), it dawned on me that I've been a bit,
shall we say, cognitively dissonant. Namely, there was a bunch of
functionality I added over the past few months that didn't have tests
yet.

Now, I really would be embarrassed if I released trivial bugs in the
code (not to speak about turning people off with their first negative
impressions), so I spent yesterday writing tests (and fixing bugs, of
course). Having EMMA coverage measurement integrated into the test
target of the build.xml helps immensely with this. Coverage is now 75%
or 76%, which means that most of the functionality is now tested and
works as I intended it to. (Of course I know that even 100% coverage
wouldn't mean it's defect-free.)

Since SourceForge download tracker showed there was one poor soul who
downloaded 0.2, I couldn't just overwrite it, hence it's now 0.3.

> Oh, yeah, and happy birthday!
>
> But seriously, is dynalang ready for exploration?

I certainly believe it is now, otherwise I wouldn't have brought it
out of the barn yet :-)

Attila.

rssh

unread,
Nov 15, 2007, 1:45:48 PM11/15/07
to JVM Languages
Ho, happy birthday ;)

Charles Oliver Nutter

unread,
Nov 15, 2007, 1:58:04 PM11/15/07
to jvm-la...@googlegroups.com

I'll have a look at it today. It may make sense to proactively implement
the interfaces in some dynamic language impl, to show how it wires in.
JRuby could be that impl.

- Charlie

Attila Szegedi

unread,
Nov 15, 2007, 3:11:39 PM11/15/07
to jvm-la...@googlegroups.com

On 2007.11.15., at 19:58, Charles Oliver Nutter wrote:

> I'll have a look at it today. It may make sense to proactively
> implement
> the interfaces in some dynamic language impl, to show how it wires in.
> JRuby could be that impl.

Definitely. I was thinking about providing an implementation for Rhino
and also for Jython (I wrote FreeMarker wrappers for Jython few years
ago, so I know the ropes in it too). It'll probably end up with
further feature requirements to handle some language oddity or other,
but that's fully expected.

Attila.

>
> - Charlie

hlovatt

unread,
Nov 15, 2007, 4:43:53 PM11/15/07
to JVM Languages
I have only briefly looked at your code, which has some great ideas in
it, so please don't take the following comment as a criticism. It is
intended more as trying to understand what you propose.

A form of interoperability that I didn't see in your examples, though
I might have missed it, is deriving classes. For example language 1
defines a class Base and language 2 wants to derive from Base. This is
assuming that both languages are OO. Can you do this?

Attila Szegedi

unread,
Nov 15, 2007, 5:43:17 PM11/15/07
to jvm-la...@googlegroups.com

On 2007.11.15., at 22:43, hlovatt wrote:

>
> I have only briefly looked at your code, which has some great ideas in
> it, so please don't take the following comment as a criticism. It is
> intended more as trying to understand what you propose.

I have a saying "talk is cheap compared to working code", and if I can
express my idea so that a computer can execute it, then it is probably
suitable (at least within this crowd) to communicate the ideas clearly
to others. So even if in the long term none of the current code would
survive in its current form, it'd still have served a purpose as a
stepping stone in an overall community effort. (Although I do
naturally hope it will evolve into something really generally useful...)

> A form of interoperability that I didn't see in your examples, though
> I might have missed it, is deriving classes. For example language 1
> defines a class Base and language 2 wants to derive from Base. This is
> assuming that both languages are OO. Can you do this?

No, there's no facility for that. The scope of my work so far was
allowing cross-language manipulation of existing object instances, and
I didn't consider cross-language class extensibility, although that's
certainly intriguing.

I actually don't even momentarily see an easy or generic way to add
this capability (I have the excuse that it's almost midnight over
here :-) ). Not all OO languages even have a concept of class -- i.e.
JavaScript uses prototype-based, not class-based inheritance.

That said, representAs() could be maybe generalized somehow. I.e. if
you provide an object/class in language B that provides some of
overrides for methods in a base class in language A (without
explicitly "extending" it), and the code in A language asks for an
instance of the base class, and is passed the object from language B,
it might examine it (duck-typing style) to see whether it can
meaningfully represent an object of the base class from A. This is
just an idea to further expand on later but might allow for easy
construction of commonly used things that are usually passed back and
forth between environments (callbacks/delegates etc.). Anyway,
midnight. If I figure out something better in my sleep, I'll write it
up in the morning :-)

(One thing you _did_ remind me of in an unrelated way is that I'd
probably need to add constructor invocation of callables, i.e.
callAsConstructor() (or callNew()) counterparts of call() methods.)

Attila.

Ted Neward

unread,
Nov 24, 2007, 4:31:58 PM11/24/07
to jvm-la...@googlegroups.com
On this subject, has anybody seen John Rose's proposal for a MultiLanguageVM
project?

Ted Neward
Java, .NET, XML Services
Consulting, Teaching, Speaking, Writing
http://www.tedneward.com

> No virus found in this incoming message.
> Checked by AVG Free Edition.

> Version: 7.5.503 / Virus Database: 269.15.34/1134 - Release Date:
> 11/16/2007 9:52 AM
>

No virus found in this outgoing message.
Checked by AVG Free Edition.

Version: 7.5.503 / Virus Database: 269.16.5/1149 - Release Date: 11/24/2007
10:06 AM

Attila Szegedi

unread,
Nov 24, 2007, 4:45:50 PM11/24/07
to jvm-la...@googlegroups.com
Yep. John actually cross posted it to this list too awhile ago.

Attila.

Charles Oliver Nutter

unread,
Nov 24, 2007, 7:21:22 PM11/24/07
to jvm-la...@googlegroups.com

I'm looking at implementing your MOP interfaces in JRuby. I'll let you
know how it goes, but I have an immediate suggestion:

Put a dynalang subdir in the tgz :) I unpacked it and it dumped a bunch
of stuff into my jruby src, lib, and so on dirs.

- Charlie

Charles Oliver Nutter

unread,
Nov 24, 2007, 8:59:08 PM11/24/07
to jvm-la...@googlegroups.com
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.

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

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

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

* There is no apparent way to represent method visibility. For the
moment, I'm implementing it to ignore the visibility of the target methods.

* 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)
- noRepresentation if any parameters are not IRubyObject instance or for
methods that don't map correctly like integer property access
- notDeletable for all deletes since attributes don't support the notion
of deletion, only setting and getting

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

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

* Why does put(Object, Object || long, Object, CallProtocol) pass
protocol but get does not?

* Plural enums and types bug me. It should be "Result", not "Results".

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

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

Charles Oliver Nutter

unread,
Nov 24, 2007, 9:23:44 PM11/24/07
to jvm-la...@googlegroups.com
Charles Oliver Nutter wrote:
> 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:

Forgot the attachment.

- Charlie

dynalang_rubyclass.patch

Attila Szegedi

unread,
Nov 26, 2007, 7:36:09 AM11/26/07
to jvm-la...@googlegroups.com

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

Charles Oliver Nutter

unread,
Nov 26, 2007, 5:45:29 PM11/26/07
to jvm-la...@googlegroups.com
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

Attila Szegedi

unread,
Nov 30, 2007, 9:47:00 AM11/30/07
to Attila Szegedi, jvm-la...@googlegroups.com

On 2007.11.26., at 13:36, Attila Szegedi wrote:

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

I assume this'd actually be:
query(Object thisObject, Object callableId, Map args)
query(Object thisObject, Object callableId, Object... args)

My question however is: what exactly is the invariant that we want to
optimize for here?
- type of thisObject
- types of arguments

Also, based on your current experience, is it acceptable to return a
callable that runs fast for the type(s) it is optimized for, but is
also able to run when passed types it is not optimized for, or should
it fail when passed unexpected types?

Attila.

Reply all
Reply to author
Forward
0 new messages