Using CLR Types

There are several issues that you will encounter when using Essence# that involve CLR types:

  • Obtaining a CLR type as a value that can be stored in a variable, passed as a parameter or sent messages;
  • Sending messages to instances of a CLR type;
  • Passing blocks as arguments to parameters requiring a CLR delegate;
  • Raising or handling CLR Events;
  • Invoking CLR methods that have "out" or "ref" parameters;
  • Converting a value from one CLR type to another;
  • Creating an array type with any desired element type;
  • Created closed generic types derived from open generic type definitions;
  • Creating instances of a CLR type

All of those things can be done in Essence#. This article will tell you how to do them.

Obtaining A CLR Type As An Essence# Value

The Essence# class CLR.System.Type (in the Essence# namespace CLR.System, which corresponds to the .Net namespace System) has many class messages that answer commonly-used CLR types. For example, the Essence# expression Type string evaluates to the .Net object that represents the .Net type System.String and the Essence# expression Type timeSpan evaluates to the .Net object that represents the .Net type System.TimeSpan. [Note: The messages string and timeSpan sent to the Essence# class that represents the CLR class Type are unique to Essence#; the actual .Net class System.Type doesn't support them. You can add Essence#-specific instance methods or class methods to any .Net class or struct; they just won't be available outside of Essence#.]

Of course, if you have an instance of a CLR type, you can just send it the message #getType (which is a message to which all .Net objects will respond.) Another way to get a CLR type is to send the message #instanceType to the Essence# class that represents CLR values having that type.

If the CLR type can’t be obtained using one of the techniques explained above, the fallback is to encode the assembly-qualified name of the type in a string, and then send the string the message #asHostSystemType, as shown in the following example:

'System.IO.ErrorEventArgs, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
        asHostSystemType

 

In most cases (but not all,) the #asHostSystemType message will fail if the fully-qualified name of the assembly is not provided. Two exceptions to that would be when the type is in the mscorlib assembly or in the EssenceSharp assembly.

Automatic Binding To Methods, Properties and Fields Of CLR Types

There is no need to do anything in order to use most of the CLR's "primitive types." The Essence# runtime system handles them automatically and transparently. Specifically, the CLR types bool, char, byte, sbyte, ushort, short, uint, int, ulong, long, float, double, decimal and string can be used in Essence# code as though they were native Essence# values--although Essence# does not have any way to syntactically express literal values that are instances of the CLR value type String, but instead uses its own mutable String and immutable Symbol values.

The Essence# run time system's dynamic binding subsystem automatically converts between Essence# String/Symbol objects and CLR String values/char array objects as needed. It also auto-converts between Essence# array objects and CLR array objects when and as needed--if possible, which it may not be (see below.) And it auto-converts CLR enumeration constants into Essence# Symbol instances whenever Essence# code sends a message to a CLR enumeration constant, and auto-converts Essence# Symbol values into CLR enumeration constants when and as needed. It also automatically performs number type conversions, and any conversions defined by implicit or explicit conversion operators defined by a CLR type. It will also convert Essence# blocks into CLR functions with the required parameter type signature.

In the case of a class whose instance architecture is #HostSystemObject, it's probable that some (and perhaps all) of the aspects defined or inherited by the CLR type represented by the Essence# class will be automatically added "on demand" (when and as needed) to the Essence# class as "virtual" methods. That happens as part of Essence#'s DLR CallSite processing. The methods that will be virtually added in this manner include methods that will get or set any non-private fields and/or properties defined or inherited by the CLR type, methods that will invoke any non-private methods defined or inherited by the CLR type that have no parameters, and methods that will invoke any non-private methods defined or inherited by the CLR type that have just one parameter. Such virtual methods are always added in pairs: The name of one will begin with a capital letter (conforming to .Net naming conventions,) and the name of the other will begin with an initial lower-case letter (conforming to Essence# naming conventions.)

There is also a small set of Essence# message selectors that will be auto-translated by the Essence# dynamic binding logic into semantically-equivalent CLR code, when such messages are sent to an instance of a CLR type and the Essence# class that represents that type neither defines nor inherits a method bound to that message selector. The full set of such message selectors is fully documented in the section on message selectors with canonical semantics, but here are a few worth highlighting:

  • size
    • If sent to any CLR array or collection, answers the size of the array or collection.
  • at: anIndexOrKey
    • If sent to any CLR array, indexable collection or Dictionary, answers the value bound to the index or key provided as the message argument.
  • at: anIndexOrKey put: aValue
    • If sent to any CLR array, indexable collection or Dictionary, sets the value bound to the index or key provided as the first message argument to the value provided by the second message argument.
  • at: aKey ifAbsent: aZeroArgBlock
    • If sent to a CLR Dictionary, answer the value bound to the key provided by the first message argument if there is one, otherwise answers the result of evaluating the second argument (as an anonymous function; the argument may be either an Essence# block object, or it may be a CLR delegate whose return type is System.Object.)
  • do: aOneArgBlock
    • If sent to any CLR array or collection, evaluates the argument once for each element of the receiver, with each such element as the argument to the block. If the receiver is a CLR Dictionary, then the elements that will be enumerated will be the values stored in the Dictionary, not the KeyValuePairs.
  • from: beginIndex to: endIndex do: aOneArgBlock
    • If sent to a CLR array or indexable collection, evaluates the final message argument for each element of the receiver in the range of indices specified by the first and second message arguments (in numerical order.)
  • keysDo: aOneArgBlock
    • If sent to any CLR Dictionary, evaluates the argument for each key of the Dictionary, with each such key as the argument to the block.
  • keysAndValuesDo: aTwoArgBlock
    • If sent to any CLR Dictionary, evaluates the argument for each KeyValuePair of the Dictionary, with the key and the value of each such KeyValuePair as the first and second arguments (respectively) to the block.

Defining User-Defined Primitive Methods

Although there are many cases where there is no need to define Essence# methods to get or set the fields or properties of an instance of a CLR type, nor to invoke its methods, such is unfortunately not always the case (as mentioned above.) The reason for that is simply because of the differences between the message name syntax used by Essence# and other CLR-based languages (and most other languages, for that matter.) Although one could theoretically have a deterministic "method name translation" function that auto-converts Essence# message selectors such as "displayOn:at:with:" to "displayOnAtWith," such "translations" would only have any significant probability of "working" when those who write the code in the "foreign" (non-Essence#) language do so with the specific intent of having the method names they use match those that would be generated by such a hypothetical Essence# "method name translation" function. Nor would it be wise to attempt to do such translations in the reverse direction. So we rejected that design in favor of an alternative: User-defined primitive methods.

Here's an example of a primitive method declaration (where the construct primitiveSpecification must be replaced by syntax that is correct for a specific type of primitive):

    doSomethingPrimitive: methodArg

        <primitiveSpecification>

        self handlePrimitiveFailure

The syntax that primitives of all types share is that the primitive specification must be enclosed by a left angle bracket on the left and right angle bracket on the right, and the angle-bracket-enclosed primitive specification must be the first (but not necessarily the only) statement in the method (and so it must immediately follow the method header and variable declaration list, if there is one.)

There are 10 different types of user-defined primitives, the syntax of which is identical to that of system primitives (which will not be discussed further here) except for what can/must occur in between the enclosing angle brackets (see above.) Method declaration syntax--including for primitive methods of both major types--is fully specified in the syntax documentation. But here is a synopsis:

The semantics of all types of primitive methods is also the same with respect to the fact that any code following the angle-bracket-enclosed primitive specification will only be executed if the primitive fails (although the term 'fails' means different things for different types of primitives.) 

In the context of a user-defined primitive, "failure" specifically refers to a dynamic binding failure (which you can think of as a failure of dynamic method dispatch). It does not encompass cases where the invoked function raises its own exception. Such exceptions are never suppressed by the run time system when user-defined primitives are successfully invoked, where "successfully invoked" means that the specified CLR method, property or field was located by the dynamic binding subsystem, and the arguments passed in were found to be type-compatible with the method's (or property's) formal parameter signature, and the dynamic invocation operation does not fail for some other reason. A user-primitive that does a divide-by-zero or attempts to reference the value of a null pointer does not qualify as a dynamic binding failure.

In the case of user-defined primitives, if there is no code that follows the "<primitiveSpecification>" construct, then the message #doesNotUnderstand: will be sent to the object that was the receiver of the message if the primitive "fails." What that means is that user-defined primitives have default "primitive failure logic" (which really sort of means "method not found" or "method not invocable with those arguments" logic,) although that default failure logic is not explicitly specified in the source code. However, the default primitive failure logic can be overridden by defining your own failure logic following the "<primitiveSpecification>" construct. There's no need to wrap the call to the primitive in an exception handler to suppress the primitive failure error; just define your own failure logic following the "<primitiveSpecification>" construct, which can be as simple as returning self, or as complex as coding the functionality of the failed primitive in Essence#.

Although user-defined primitives will work when used with any Essence# class, the rest of this discussion assumes that they are being used with instances of CLR types as their receiver, and are not being used with instances of "native" Essence# objects.

There is more detailed information on configuring Essence# classes so that they represent a CLR type (and thereby govern the behavior of instances of that type when Essence# code sends messages to those instances, and so that they have the ability to create instances of that type) in the documentation of classes and namespaces.

Type conversion Primitives

A type-conversion primitive can be used to convert an instance of one CLR type to that of another.  The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <convertTo: CLRTypeName>

The primitive will fail if the CLRTypeName cannot be resolved to a CLR type when the primitive is invoked. The name may be as syntactically simple as String or as complex as a fully-specified assembly-qualified type name. The more information provided in the type name, the more likely the primitive will succeed.

Note: Essence# cares far less about the "type" of a value than the CLR does, but even so, converting from one type to another may change which operations are available, how the available operations will work, and which primitives (e.g, native CLR methods) will accept the value as an argument.

GetField Primitives

A getField primitive can be used to get the value of a named field (instance variable) of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <getField: fieldName>

The fieldName identifier must exactly match (including capitalization) the name of a field defined or inherited by the receiver's CLR type.

GetProperty Primitives

A getProperty primitive can be used to get the value of a named property of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <getProperty: propertyName>

The propertyName identifier must exactly match (including capitalization) the name of a property defined or inherited by the receiver's CLR type.

SetField Primitives

A setField primitive can be used to set the value of a named field (instance variable) of a CLR object or value   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <setField: fieldName>

The fieldName identifier must exactly match (including capitalization) the name of a field defined or inherited by the receiver's CLR type.

SetProperty Primitives

setProperty primitive can be used to set the value of a named property of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <setProperty: propertyName>

The propertyName identifier must exactly match (including capitalization) the name of a property defined or inherited by the receiver's CLR type.

InvokeField Primitives

An invokeField primitive can be used to invoke a delegate (CLR anonymous function) stored in a named field of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <invokeField: fieldName>

The fieldName identifier must exactly match (including capitalization) the name of a field defined or inherited by the receiver's CLR type.

The arity (number of method parameters) of the Essence# method and the invoked CLR delegate (anonymous function) must be the same, as must the sequence of the parameters.

InvokeProperty Primitives

An invokeProperty primitive can be used to invoke a delegate (CLR anonymous function) returned by a named property of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <invokeProperty: propertyName>

The propertyName identifier must exactly match (including capitalization) the name of a property defined or inherited by the receiver's CLR type.

The arity (number of method parameters) of the Essence# method and the invoked CLR delegate (anonymous function) must be the same, as must the sequence of the parameters.

InvokeEvent Primitives

An invokeEvent primitive can be used to invoke a CLR event defined (or inherited) by the type of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <invokeEvent: eventName>

The eventName identifier must exactly match (including capitalization) the name of an event field or property defined or inherited by the receiver's CLR type.

The arity (number of method parameters) of the Essence# method and the invoked CLR event handler delegate must be the same, as must the sequence of the parameters.

InvokeMethod Primitives

An invokeMethod primitive can be used to invoke a method defined (or inherited) by the type of a CLR object or value.   The primitive will succeed or fail for all the same reasons the same operation would if performed using C# code.

Example primitive spec (with enclosing angle brackets): <invokeMethod: methodName>

The methodName identifier must exactly match (including capitalization) the name of a method defined or inherited by the receiver's CLR type.

The arity (number of method parameters) of the Essence# method and the invoked CLR method must be the same, as must the sequence of the parameters.

CreateInstance Primitives

A "create instance" primitive is actually just an invokeMethod primitive whose "method name" argument is the identifier new. It can be used to create an instance of a CLR type (if such an operation is considered legal by the CLR, which it may not be.) The message receiver must be an Essence# class that represents a CLR type.  In other words, this primitive must be an Essence# "class method" (a method defined by the metaclass of the class.) The primitive works by binding to and invoking one of the constructors defined by the CLR type represented by the canonical instance of the receiver (which is a metaclass.)

Example primitive spec (with enclosing angle brackets): <invokeMethod: new>

The arity (number of method parameters) of the Essence# method and the invoked CLR constructor must be the same, as must the sequence of the parameters. 

Creating Instances Of CLR Types

If a CLR type has a zero-argument constructor, you can create an instance of it by sending it the message #new. If it has a one-argument constructor, then you can create an instance of it by sending it the message #new: anArgument, where anArgument must be type-compatible with one or more the type's one-argument constructors, where "type-compatible" means:

  • a) the constructor's parameter type is assignable from the type of the run time argument, or
  • b) there's an implicit or explicit type conversion operator defined from the type of the argument to the type of the constructor's formal parameter, or else
  • c) the Essence# dynamic binding subsystem supports conversion from the argument type to the parameter type (as explained both above and below.)

Invoking type constructors that have two or more arguments requires defining a user-defined primitive for the Essence# class that represents the CLR type whose instances are to be created, as explained in the section on user-defined primitives (above.)

Alternatively, you can send an instance creation message to the Essence# class that represents a CLR type. There are three ways to get that Essence# class:

Sending the message #new to the Essence# class that represents a CLR type is equivalent to sending the message #new to the type itself. The same applies to sending the message #new: anArgument to an Essence# class that represents a CLR type.

Other instance creation messages, of a more complex nature, which will create instances of a CLR type can also be sent to an Essence# class that represents a CLR type, but user-defined primitive methods must be defined for each such message, as explained in the section on user-defined primitives (above.)

Argument/Parameter Type Conversion

Formally–in other words, in theory–Essence# is dynamically typed: The “type” of an expression is simply the powerset of all messages that can be sent to the expression without causing a method-binding error. And that is also true in practice, in that the compiler permits any message to be sent to any expression, and in the fact that the compiler permits any expression to be assigned to any variable or passed as any argument in any message, and in the fact that typing errors are only discovered and reported at run time.

But that does not mean that any expression can be passed as any argument in any message to any receiver without causing any run-time type errors, even in cases where the only messages that will be sent to the value of an argument won’t result in a method binding error. That constraint does hold in many other implementations of dynamic languages, and it does hold when the method that will be invoked by the message is an Essence# method. But it does not hold when the “method” that will be invoked is a method defined by a CLR type.

As should be clear from reading the above material, in Essence# a method of a CLR type is effectively just a user primitive–although the user primitive may be a virtual method that does not need to be formally defined as a method in the Essence# source code, because the dynamic binding subsystem automatically binds Essence# messages to the methods of CLR types that require less than 2 arguments, to the constructors of CLR types that require zero arguments or that require only argument, and to any non-private properties or fields of a CLR type.

It is generally the case in all dynamic language implementations that “primitive methods” will fail if the types of the arguments are not what the (statically-typed) primitives require–even if the primitive could have performed its intended operation simply by sending (using dynamic message dispatch) the right messages to the argument. Primitives generally don’t send messages to their arguments using dynamic message dispatch. And the methods of CLR types certainly do not.

Fortunately, the Essence# dynamic binding system will–if it can–automatically do the required type conversions for you: As explained above in the section on virtual method binding, the run time system's dynamic binding subsystem automatically converts between Essence# String/Symbol objects and CLR String values/char array objects as needed. It also auto-converts between Essence# array objects and CLR array objects when and as needed--if possible, which it may not be (see below.) And it auto-converts CLR enumeration constants into Essence# Symbol instances whenever Essence# code sends a message to a CLR enumeration constant, and auto-converts Essence# Symbol values into CLR enumeration constants when and as needed. It also automatically performs number type conversions, and any conversions defined by implicit or explicit conversion operators defined by a CLR type. It will even convert Essence# blocks into CLR functions with the required parameter type signature.

But the Essence# dynamic binding system isn’t magic. It can’t handle all cases. It’s simply not possible to convert any type into any other type. And even when it is, the run time system may not have the information required to do it correctly. And in other cases, the logic to do the required conversion simply hasn't been implemented, for one reason or another.

CLR Array Types

One common case where a conversion might be possible, but the dynamic binding system doesn’t attempt to do it, involves arrays. From the point of view of the CLR, an Essence# array is an array whose elements have the type System.Object (which is how the CLR sees an instance of the Essence# class Array,) or the type System.Byte (a ByteArray,) or the type System.UInt16 (a HalfWordArray,) or the type System.UInt32 (a WordArray,) or the type System.UInt64 (a LongWordArray,) or the type System.Single (a FloatArray,) or the type System.Double (a DoubleArray,) or the type System.Decimal (a QuadArray) or the type String (a Pathname.)

Unfortunately, there are many methods of CLR types that require an array having elements of some type other than the ones supported by Essence#. If the CLR method parameter requires an array whose element type is not the same as that of the corresponding argument at run time, then the binding to the method will fail.

Fortunately, if the conversion of the array to one with the required type is possible (because all the elements can be converted to the required CLR type,) you can code that conversion yourself in Essence#. The following example shows how:

| clrSourceArray elementType arrayType argument|
		
"Step 1: Convert the Essence# array into a CLR array:"
clrSourceArray := #('one' 'two' 'three') asHostSystemArray.

"Step 2: Get the desired element type of the array to be passed as an argument:"
elementType := System.Type string.

"Step 3: Create a CLR array type with the necessary element type:"
arrayType := elementType makeArrayType.

"Step 4: Create the CLR array having the correct size and element type:"
argument:= arrayType new: clrSourceArray size.

"Step 5: Copy the elements of the source array into the array that will be used as an argument:"
1 to: clrSourceArray size do: [:index | argument at: index put: (clrSourceArray at: index)].
^argument

 

Or you could just send the message #asHostSystemArrayWithElementType: to the Essence# array, as in the following example:

#('one' 'two' 'three') asHostSystemArrayWithElementType: System.Type string

Passing Blocks As Arguments To Parameters Requiring A CLR Delegate

Please see the article on Passing Blocks As Arguments To Parameters Requiring A CLR Delegate.

Raising And/Or Handling CLR Events

Please see the article on CLR Events.

Handling "out" or "ref" parameters

Please see the article on invoking CLR methods that have “out” or “ref” parameters.

CLR Generic Types

Please see the article on using CLR Generic Types.

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

Last edited Aug 10, 2014 at 5:54 PM by Strategesis, version 70