This project has moved. For the latest updates, please go here.

CLR Generic Types

The CLR has three types of “generic type”: Open generic types, partially-open generic types and closed generic types. An open generic type is also called a generic type definition. An open generic type is one where all of its generic type parameters remain “open” because none of them have been bound to a specific type argument. A partially-open generic type is one where some, but not all, of the type’s generic type parameters have been bound to specific type arguments. A closed generic type is one where all of the type’s generic type parameters have been bound to specific type arguments. For the most part, a partially-open generic type is essentially the same as an open generic type. The distinction that really matters is between closed generic types and ones that have generic type parameters that aren’t bound to a specific type.

It’s possible to create instances of closed generic types, but it is not possible to create instances of open or partially-open generic types. And that can be a problem, because .Net class libraries typically only provide generic type definitions that define open generic types. So, although you can define an Essence# class that represents a .Net type that is an open generic type definition, you won’t be able to use that Essence# class to create instances of the type by sending it the normal instance creation messages (e.g, #new or #new:). Creating instances of such a type requires providing one or more types that will be used as the type arguments to construct a closed generic type from the open generic type defined by the .Net class library.

Constructing Closed Generic Types

Fortunately, you can use Essence# to construct a closed generic type from an open generic type definition, as illustrated in the following example:

| openGenericDictTypeDefinition esClass closedGenericType |
openGenericDictTypeDefinition := 
        'System.Collections.Generic.Dictionary`2' asHostSystemType.
esClass := openGenericDictTypeDefinition asClass.
closedGenericType := esClass 
                        instanceTypeWith: Type string 
                        with: Type string.
dict := closedGenericType new.
dict at: #foo 
	ifPresent: 
                [:value | 
                        System.Console 
                                write: 'The value at #foo is '; 
                                writeLine: value
                   ] 
	ifAbsent: 
                [
                        System.Console writeLine: '#foo is not present'
                ].
dict at: #foo put: #bar.
dict at: #foo 
	ifPresent: 
                [:value | 
                        System.Console 
                                write: 'The value at #foo is '; 
                                writeLine: value
                   ] 
	ifAbsent: 
                [
                        System.Console writeLine: '#foo is not present'
                ].

 

As mentioned in the article on using CLR types, the Essence# class that represents a CLR type can be obtained by sending the message #asClass to the CLR type object.

If an Essence# class represents a generic type (whether the type is open, partially-open or closed makes no difference,) then you can create a closed generic type by sending one of the following messages to the Essence# class: #instanceTypeWith: aCLRType, #instanceTypeWith:aCLRType with: aCLRType, #instanceTypeWith: aCLRType with: aCLRType with: aCLRType ,  , #instanceTypeWith: aCLRType with: aCLRType with: aCLRType, #instanceTypeWith:aCLRType with: aCLRType with: aCLRType with: aCLRType with: aCLRType or #instanceTypeWithAll: anArrayOfCLRTypes.

Defining Essence# Classes That Represent Closed Generic Types

One good way to handle the case where the .Net type you want to use is an open generic type definition would be to define a subclass of an Essence# class that represents that type, and then use one of the messages above to construct a closed generic type that will be the instance type of the subclass.

Another good way to do it is simply to use the CLR’s syntax for closed generic types when specifying the instance type of the subclass. The following examples show both approaches:

"Class creation/configuration using the Essence# #instanceTypeWith: message"
	
	| superclass class |
        superclass := 'System.Collections.Generic.List`1, mscorlib'
                                asHostSystemType asClass.
        class := Class new.
	class 
                superclass: superclass;
                instanceType: (superclass instanceTypeWith: System.Type object).

 

"Class creation/configuration using the CLR's syntax for closed generic types"
| class |
class := Class new.
class
assemblyName: 'mscorlib';
hostSystemNamespace: #System.Collections.Generic;
hostSystemName: 'List`1[[System.Object]]'

 

Open Generic Type Definitions

It's also not only possible to define an Essence# class that represents an open generic type definition, the system actually does that for you automatically. And there's a reason it does so:

An Essence# class that represents any closed generic type inherits from the Essence# class that represents the corresponding open generic type definition. For example, the class Smalltalk.Set–which represents the .Net type System.Collections.Generic.HashSet<Object>)–inherits from the Essence# class that represents the .Net type System.Collections.Generic.HashSet<>.  

And that matters because best practice would be to define most of the user-primitives used to adapt a generic type for use by Essence# in the Essence# class the represents the open generic type definition, instead of in ones that represent any of the corresponding closed generic types.

To define an Essence# class that represents an open generic type definition in an Essence# class library, the default way to do that is to use the CLR's name for the open generic type definition as the name of the class--even though that class name is not a legal global variable reference. In the case of the open generic type definition System.Collections.Generic.HashSet<>, the CLR name is System.Collections.Generic.HashSet`1 (which you will find already defined for your in the namespace CLR.System.Collections.Generic in the Essence# Standard Library.)

The essence of OOP: It's all messages, all the time.

Last edited Aug 10, 2014 at 1:23 AM by Strategesis, version 19