[rdnzl-devel] Patch for generic types in RDZNL

Iver Odin Kvello iverodin at gmail.com
Thu Feb 14 00:21:15 UTC 2008


I've attached a patch that adds support for generic types to RDNZL;
the approach used might not be ideal, I'm not sure.

The problem with generics in RDNZL was mainly an issue in GetType
making the "BaseType´n[ParameterType1, ...,ParameterTypen]"-syntax
fail to construct the correct type if the base type and parameter
types lived in different assemblies. Here is an example:

RDNZL-USER 25 > (invoke "System.Type" "GetType" "System.Action`1[System.Int32]")
#<RDNZL::CONTAINER System.Type #xBD5B28>

RDNZL-USER 26 > (invoke "System.Type" "GetType"
"System.Action`2[System.Int32,System.Int32]")
Warning: Returning NULL object from .NET call
NIL

Even if it had worked, some syntax would have been nice to let this
interact with USE-NAMESPACE.

Now, if all the type-names involved in a generic type-name like above
were fully assembly-qualified, GetType would find the correct type.
For the above type that would be
"System.Action`2[[System.Int32, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32,
mscorlib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]], System.Core, Version=3.5.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"

To make it easier to refer to this type, and to make it interact
better with USE-NAMESPACE, I propose allowing a list (tree) to denote
generic types. Since the length of the list will encode the number of
parameters, this also allows one to drop the `n-part of the typename,
and denote the type above like this (with the System namespace used:)

  '("Action" "Int32" "Int32")

The patch implements this syntax in a particular way that might be
debatable on several points. It modifies RESOLVE-TYPE-NAME to accept a
list of this format, returning a new list of each type resolved (and
hashed). That is, a tree of type names is mapped to a tree of fully
qualified type names, with the first element in each list (and
sublist) treated specially. Further, it modifies MAKE-TYPE-FROM-NAME
so that this too accepts a tree of types, and if so, creates a closed
generic type from the base type and parameters. To do this it first
retrieves the base type, then calls MakeGenericType on it.

All functions that can take a string as a type-name is further
modified to accept also a tree, passing this on to RESOLVE-TYPE-NAME.
This allows one to do stuff like

   (new '("Func" "Int32" "String") (lambda (x) (format nil "~@r" x)))

and so forth. This all works:

 RDNZL-USER 31 > (setf f (new '("Func" "Int32" "String") (lambda (x)
(format nil "~@r" x))))
 #<RDNZL::CONTAINER System.Func`2[[System.Int32, mscorlib,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089],[System.String, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
#x3BDE2C8>

 RDNZL-USER 32 > (invoke f "Invoke" 2008)
 "MMVIII"


The debatable points:

 1. It might have sufficed to just have made RESOLVE-TYPE-NAME create
the fully qualified typename directly. This would have either implied
doing some string manipulation (like a function I posted earlier) or
by creating the type using MakeGenericType and returning the
AssemblyQualifiedName. The first alternative didn't feel quite right
and the second would be pretty much what I've implemented except that
the type would be thrown away after its name was resolved. I thought
perhaps it was a bit neater to modify both RESOLVE and
MAKE-TYPE-FROM-NAME, keeping the basic function of each basically like
the current implementation.

 2. Having every function accept trees makes it often neccessary to
tests for CONTAINERP, STRINGP and finally CONSP which perhaps isn't
that nice.

 3. INVOKE accepts a syntax (INVOKE (assembly . type-name) method)
where the static method named is called on the type, resolved from
type-name using only the assembly given. I extended this to also
accept generic types, but how to resolve the parameter-types isn't
then completely obvious (the implementation just uses the normal
type-lookup for parameter-types.) Also, this function already used
CONSP for the type-specifier (compatibly, though).

4. Perhaps using lists like this is to 'bland' syntactically. It might
do to have an operator like
     (generic "Func" "Int32" "String")
to return a closed generic type, and make sure all callers accepted
the resulting type-object. This would leave RESOLVE-, MAKE-TYPE-FROM
and the rest as they were, doing all the extra work in a single spot.
But this would pretty much be a type-name too, and one extra word
longer than absolutely necessary:
     (new (generic "Func" "Int32" "String") (lambda (x) ...))


If the patch looks acceptable however, I'll of course update the
documentation too.

This contains no support for generic methods. The reason is that these
are even more problematic to handle using the given interface. In
particular, GetMethod will not retrieve the method (or anything) if a
generic method is overloaded; so one has to implement a search
manually before MakeGenericMethod can be called
(http://blogs.msdn.com/yirutang/archive/2005/09/14/466280.aspx). But I
think this is still useful mostly for toying around with the newer
C#-features, so proper support can probably wait.

Regards, Iver
-------------- next part --------------
A non-text attachment was scrubbed...
Name: rdnzl-generics.patch
Type: application/octet-stream
Size: 12418 bytes
Desc: not available
URL: <https://mailman.common-lisp.net/pipermail/rdnzl-devel/attachments/20080214/5ebda49e/attachment.obj>


More information about the rdnzl-devel mailing list