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