[IronPython] CLS compliance

Dino Viehland dinov at exchange.microsoft.com
Wed Mar 1 02:24:51 CET 2006

Great summary of the problem space.  First, let's look at your first Point class.  One aside on the CLR: we do have the concept of value types & reference types so int is a primitive.  But the CLR also supports boxing (and C# has auto-boxing) so we can just box that int up into an object.  We can then do the cast back to an int which will unbox it (the cast is also C#'s syntax for unboxing value types).

We currently have 2 different ways in which built-in functions are called.  One is via our ReflectedMethod class which uses a late-bound dynamic invocation after using reflection to match parameters (including overload resolution).  The other code path is our optimized path using BuiltinFunction's where we generate a dynamic method that will perform the method binding and do an early-bound (early here being defined as at the time the dynamic method is generated, but it still doesn't happen at call time).  

That has me jumping to the end of your mail:  this is how we handle CLS consumption.  We also allow CLS extension (you can derive from built-in types) by dynamically creating a single CLS class that derives from the base class (actually a single class for each object/interface combo).  This class will override all of the virtual methods w/ a helper function that will check if the user has replaced the function from Python.  Therefore:

class foo(list):
	def __getitem__(self, index):
		return 'abc'

and you pass an instance off to C# code:

	object ConsumesAList(IList foo){
		return foo[0];

returns 'abc' and not the IronPython List's implementation's get_Item.  So that's how you can actually inherit from a built-in type in the CLR.

Ok, so now we get to the production side of things...  All the options you mention are possible...  For our CodeDOM implementation we will consume @accepts and @returns syntax (as described in PEP 318 and implemented in the non-standard typecheck library).  This is the 'extra decoration' approach to getting the type information. 

Inference is nice but it also has additional problems.  Even if we infer the types you're using or expecting correctly, how do we know those are the types you want?  Maybe you really want to expose something that accepts objects!  And ultimately exposing APIs that only take objects isn't that bad.  It allows C# code to call the Python code, and it allows the Python code to receive anything it wants.    The C# programmer is a little upset because they don't get their rich type info but that's tough for them, Python APIs will accept any inputs.

The other interesting case here is inheritance where we presumably do know the real type information, and can therefore generate correct overrides.

But I don't personally see that as the biggest problem.  I'd be happy to live in a world where my Python code only exposed object signatures, at least as a starter solution.  To me the biggest issue is how can we maintain Python compatibility while at the same time creating this static-type view of the Python world.  

That problem starts with class definitions (in Python class definitions are executable statements) and carries through to doing things like changing a method at runtime (where a normal CLS producer should call the changed method, not the statically compiled method).  And obviously there's lots of other challenges in this space as well - hence the reason we don't yet have a solution here :)

Do you want to help develop Dynamic languages on CLR? (http://members.microsoft.com/careers/search/details.aspx?JobID=6D4754DE-11F0-45DF-8B78-DC1B43134038)

-----Original Message-----
From: users-bounces at lists.ironpython.com [mailto:users-bounces at lists.ironpython.com] On Behalf Of Peter Mechlenborg
Sent: Tuesday, February 28, 2006 1:00 PM
To: Discussion of IronPython
Subject: Re: [IronPython] CLS compliance

Dino Viehland wrote:
 > IronPython can consume CLS types and extend them, but cannot produce
 > new CLS types.  Unfortunately that last part is extremely tricky
 > because CLR types are not dynamic, but Python types are.  It is
 > something that's on our radar but we don't yet have a solution for it.

Thanks for the quick answer.

Up front I must say that my knowledge about Python and CLR is limited,
so I hope that the following is not completely wrong.

My understanding is that the general way of targeting a dynamic
language towards a static VM/language is to escape through the common
root type, i.e., Object, and then use runtime casts to perform runtime
type checks.  The following code fragment should illustrate this
through the use of a Point class, using Java-like syntax:

   class Point {
     Object x,y;
     void move(Object xx, Object yy) {
       x = (int)x + (int)xx;
       y = (int)y + (int)yy;

The fields and arguments are qualified by Object, but cast to integer
before the addition in the move method.  I'm assuming that integers
are objects and not primitive types, I'm not sure how it would look if
integers were primitive.

If the above code was generated from a untyped language, and we wanted
to expose the move method and the Point class through the CLS, we need
some extra type information.  I'm not sure how to do this, but ideas
could be optional type declarations, some kind of IDL, or type
inference, and in my eyes this is the biggest problem when trying to
produce CLS compliant code from a dynamic language.  If we have the
extra type information we can create wrapper methods with more
specific type declarations:

   class Point {
     Object x,y;
     void cls_move(int xx, int yy) {
       this.move((Object) xx, (Object) yy);
     void move (Object xx, Object yy) {

Here cls_move should be used by foreign languages, while move should
be use natively; in general giving objects two separate interfaces,
one for foreign code, and one for native code (I'm regarding the
fields as private).

What I sketched above is properly too simplistic, but would the
general idea work of wrapper methods work for code produced by
IronPython?  (Maybe the hardest problem is all the little details).

I'm quite interested in how you have implemented consumption of CLS
types, because I don't see an obvious way of doing this.

If we reverse the Point example:

   class Point {
     int x,y;
     void move(int xx, int yy) {
       x = x + xx;
       y = y + yy;

Point is now written in a static language, and we would like to use it
from IronPython.  In this case all arguments passed from the callee to
the move method must be qualified as int, but in IronPython the type
of the arguments are not known.

One solution is to create wrapper objects around all foreign objects,
and that way provide a single location that can translate dynamic
calls into static calls.  This however adds extra overhead, and also
complicates inheritance because the wrapper would have to use
delegation, which does not preserve the self/this reference, which
again conflicts with overriding of methods.  I think delegation would
be needed because CLS only supports single inheritance for classes.
It might also be possible to use reflection, and make all calls to
foreign objects in a reflective way.  Am I missing something obvious?
What is your general strategy for consumption of CLS types in

 > Do you want to help develop Dynamic languages on CLR?
 > (http://members.microsoft.com/careers/search/details.aspx?JobID=6D4754DE-11F0-45DF-8B78-DC1B43134038)

It does seem like a very interesting job.  Its nice to see some focus
on dynamic languages.

Have fun,

   --  Peter
users mailing list
users at lists.ironpython.com

More information about the Ironpython-users mailing list