[Mono-devel-list] Re: Dynamically changing classes?

Paolo Molaro lupus at ximian.com
Thu Sep 25 09:36:10 EDT 2003


On 09/25/03 Thomas Sondergaard wrote:
> > Problem: it doesn't handle updating existing class instances.  Thus,
> > this may be unsuitable for some situations.
> 
> Yes it is a trick and it is nasty. I think it is fair to conclude that the
> current class model in .net is not friendly to the idea of classes that
> change their interface and/or implementation at runtime.

I think you're mixing two things: language implementation and
interoperability with other languages in the CLR. This is the cause of
much confusion: the two things should be addressed separately or you'd
end up saying that C is not an adequate language to implement a dynamic
language, because C doesn't have classes/interfaces/whatever. The truth
is most dynamic languages are implemented in C just fine:-)
In the CLR a class can't be changed at runtime the same way a package
can be done in perl or other dynamic languages: adding/removing methods,
changing the parent class etc. This is not an issue for the
implementation of the dynamic language in the CLR, though, it's only an
issue of interoperability. So let's start with the implementation first
and discuss the interoperability issue later.

Since the CLR offers basically the same features that are available to a
C programmer, if a dynamic language can be implemented in C, it can be
implemented in the CLR, too. There may be a few drawbacks (higher memory
usage or it can be a little slower), but there are also benefits
(interoperability with other languages, first of all, but also access to
a GC, a JIT etc.).

Just porting from the C code, though possible, is not going to give access
to most of the useful features of the CLR, but which features are useful
for a particular dynamic language depend on the langauge needs.
For example, the CLR provides a nice (and fast) way to do
single-inheritance, but if it's not enough for a language, the language
implementor can always implement its own mechanism. So, again, this is
not an implementation issue, it's just an interoperability issue.

In Perl you have multiple-inheritance and the parents of a type can be
changed at runtime. Now, *implementing* the feature on the CLR is very
easy: each perl package can be its own CLR type. The base type of the
CLR type doesn't matter, it could be System.Object or a perl-specific
class such as Perl.UNIVERSAL or Perl.RV. This type would have an array
of types that correspond to the @ISA array in perl-land. The isa method
is easily implemented with a linear search in this array (though of
course, more clever methods can be used as well). As we see, there is no
issue in implementing the feature, even if it doesn't map to the CLR
feature.
The same can happen with methods: the CLR type can have a hashtable
that maps from method names to perl subroutines (exactly like the C
implementation of Perl) and the nethod invocation will just do a lookup
in the hash. This reproduces the needs for a dynamic language like perl.
So, most of the features that are common to many dynamic languages can
be easily implemented in the CLR.

Now, let's see about the other issue: interoperability. You can be a
consumer or a producer, so let's see first how to consume CLR
methods from a dynamic language. In perl a call to Console.WriteLine
could look like this:

	System::Console->WriteLine ("hello world!");

The perl compiler could generate the following code (in C# syntax):

	IObject console = get_object_from_type_name ("System::Console");
	console.send ("WriteLine", new object[] {"hello world!"});

The console.send implementation would do:

	object send (string name, object[] args) {
		clr_type.InvokeMember (name, null, args);
	}

This is what a simple compiler would do and I agree this wouldn't be
very fast, but a slightly smarter compiler can do better: the
get_object_from_type_name() call can be done at compile time, for
example (this optimization is done also by the current C Perl
implementation). Once System::Console is mapped to a CLR type,
the compiler could also get the MethodInfo and invoke on it directly
(much faster than InvokeMember). An even better compiler can
add a couple of runtime checks and the IL code to call Console.WriteLine
directly, depending on the argument types.

How would you expose Perl methods to other languages? One way is to
just expose an IObject-like interface (with params object[] args) so
that calling a method in a perl object would be almost natural:

	// C# code to call a method on a perl-object
	perl_object.send ("do_something", "with" "these", "arguments");

Another one can be to generate methods in IL that can be called directly
in C#. The implementation can be very simple:

	// could have also overloaded versions with different types
	void do_something (params object[] args) {
		send ("do_something", args);
	}

> > Just have all your internal classes implement IDynamicLanguage, and you
> > get the same functionality as "send_msg".  There's no particular reason
> > why this has to be in CIL, as opposed to doing it yourself (above).
> 
> Not really, by implementing this 'in .net' not as .net you are bound to
> execute a lot slower. Imagine compiling this ruby function to .net
> 
> class ACLass
>     def invokeSomeMethodsOnArg(arg)
>         arg.doThis
>         arg.doThat
>         arg.AlsoThis
>     end
> end
> 
> Ruby is dynamically type so arg can be any type. This means that the only
> way to invoke doThis, doThat and AlsoThis is using the reflection API, e.g.
> using arg.GetType().InvokeMember("doThis", ....). This will be very slow.

Nope, you can easily implement it another way. In C# syntax it could be:

	IObject obj = arg as IObject;
	if (arg != null) {
		arg.send ("doThis", null);
		arg.send ("doThat", null);
		arg.send ("AlsoThis", null);
	} else {
		// slow path
		arg.GetType().InvokeMember("doThis", ...);
	}

A smart compiler can optimize the slow path as detailed above (maybe
also making use of profiling information or some sort of cache).

> Not only because of the services rendered I think, but because of the layers
> of abstraction, which could be done away with if the CLR supported the
> concept. The abomination known as method overloading adds complexity to this

I don't see what concept you think the CLR should support, but if you think you
need some kind of support from the CLR, mono is sure a good platform to
prototype it in:-)
Sure, InvokeMember() is bound to be slow (maybe we could see if we can
speedup the mono implementation), but, for example, MethodInfo.Invoke()
is a lot faster (it's currently 2-3 times slower than a ruby/perl method
call and it could be optimized, probably). But those are not concept
issues, just performance tuning issues.

lupus

-- 
-----------------------------------------------------------------
lupus at debian.org                                     debian/rules
lupus at ximian.com                             Monkeys do it better



More information about the Mono-devel-list mailing list