Replies: 4 comments 2 replies
-
|
Several months ago I posted an analysis of type lookup performance in ClojureCLR: Are you my type?. Certainly relevant in this context. Note that the proposal for a modified lookup mechanism at the end of that post would need to be modified somewhat if we were to adopt the aliasing mechanism proposed above. |
Beta Was this translation helpful? Give feedback.
-
|
I like the macro solution via |
Beta Was this translation helpful? Give feedback.
-
|
We need a clarificaion of what a "type alias" actually defines. Two models come to mind:
An example to show the distinction.
By our usual method of referring to types using CLR typename syntax, we would expect the associated value for There is no type There are seventeen generic definitions of One can think of ways to make a symbol association work. However, I argue that we should implement symbol-to-type mapping instead of symbol-to-symbol mapping. Namespaces already supply a mechanism for mapping symbols to types.
Use of the symbol-to-type map is embedded all over the Clojure interpreter/compiler code. I've written a little about this: But these don't give the full complexity. If you want to dig deeper, look at the code for
By building on the existing namespace mechanism for symbol-to-type mapping, we automatically participate in all From a coding simplicity viewpoint, this approach also fits easily into the type resolution algorithm used in the parsing process. |
Beta Was this translation helpful? Give feedback.
-
|
Rethinking the need for the To me, there are several primary gains to achieve with this proposal:
Without the
I can live with having to specify the backquote-arity for generic type definition references. versus |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Proposal: a simplified type-naming standard for ClojureCLR
Background
ClojureCLR's current mechanism for specifying types is cumbersome. It follows the CLR conventions, which are not user-friendly, rather than something like you see in C# or F#.
For example,
The CLR type naming syntax is quite complicated. It has to accommodate generic types, generic argument types, arrays, pointer designators, etc. Moreover, typenames can be fully assembly-qualfied, including aseembly name, assembly version, assembly culture, etc. Behold the lowly 64-bit integer:
There is a fairly general mechanism for constructing types already in place in ClojureCLR. Check out ClrTypeSpec.
I
stoletook inspiration from the code for this in Mono. (This was back in the day before Microsoft open-sourced this kind of thing.) There are two parts to this code:Now that Microsoft has open-sourced much of the .NET/CLR code, we can look at how Microsoft handles typename parsing. More below.
Goals
The primary goal:
The desired qualities:
\N` syntax for generic typesintandlongdefined by Clojure itself.Some constraints to respect:
nsconstruct or its dependencies, such asimport. (The Clojure chiefs seem to react coldly to any suggestions of modifications here. Also, there are libraries that analyzensdeclarations that would be affected.)We can do this.
The actual extensions are defined in goals (G3) and (G4). The former is a question of syntax; the latter is a question of typename resolution We consider each in turn.
Generic types
The goal is to get rid of the requirement of explicitly including backquote-numeral in generic type names, whether generic type definitions or constructed generic types. In other words, get rid of the '`2' in
However, this causes a problem. We would not be able to tell if
is referring to a generic type
Twith one generic type argument or an array of typeT(non-generic).The simplest solution is to follow what C#, F#, and numerous other languages do: Use angle brackets for generics. Thus:
TTT[]T[]T[,,]T[,,]T<>T`1T<,>T`2T<U,>T `2[U,]ClrTypeSpecmechanismT<U,V>T`2[U,V]Regarding our constraints:
nscertainly not involvedType aliases
We'd like to be able to specify a type in this way:
The current typename handler has no problem parsing this. The problem is in resolving the types named in it:
Dictionary,int,String.And they present three distinct problems.
Internal Clojure names
Names such as
int,long,shorts, etc. are not part of the CLR specification. Languages such as Clojure, C#, and F# define standard type aliases. (And don't necessarily agree on the names. C# hasdoubleandfloatfor 64- and 32-bit floating point types; F# has bothdoubleandfloatfor 64-bit, andfloat32andsinglefor 32-bit. I have been caught out by this.)Internal names can be incorporated fairly easily into the type resolver. We can consult an existing map from the pre-defined typename strings to the corresponding type.
Namespace-mapped types
You might have noted a discrepancy in how some type names are treated.
For example, if you are providing type hints, this works just fine:
but
System.Collections.Generic.List`1[String]will fail. The explanation is straightforward. The type resolver in ClojureCLR makes several checks on a symbol before passing it to
ClrTypeSpec.One check is to see if it is a special name such as
intorlong. A second check is look up the symbol in the current namespace's alias map.The alias map maps symbols to types. A third check is to call
System.Type.GeType(String). There are a few other special cases that don't concern us. After all of that, is we've not gotten a hit, theClrTypeSpecparser/resolver is called.The reason the first example succeeds is that
Stringis entered in every namespace's alias map. When a namespace is created, it is set up with set of default mappings. The code runs through loaded assemblies and looks for all public classes/interfaces/value-types with a namespace ofSystem(with a few other restrictions) and maps the basic type name (no namespace qualifier) to the type. Thus the symbolInt32gets mapped to the typeSystem.Int32; the symbolStringgets mapped to the typeSystem.String.To make our second example work, we need to make the
ClrTypeSpecresolver be namespace-aware. Resolving must be done with respect to supplied namespace. That namespace's alias map is consulted before trying other measures (but after checking for built-ins likelong).Other aliases
To get all the way to
we need a way to supply meaning to
Dictionary.The easiest way is to type into the same namespace alias map referenced in the previous section. The standard way to add symbol-to-type entries to the map is via
import. This can be done by direct call or by an:importclause in annsdeclaration:maps symbol
Pathto typeSystem.IO.Path. This won't work for our case. The symbol thatimportmaps is the name of the type.If we do
we will get an mapping from symbol
|List1| to the collection type.If we subsequently try to do
we'll get
Constraint (C1) forbids messing with
import. It is easy enough to add our own macro:(add-type-alias Dictionary |System.Collections.Generic.Dictionary<,>| )After which, our example
is now resolvable.
BTW, this would allow a sequence such as this:
Then you could do something like:
Note: this is quite easy to implement. The macro can expand into a call to
clojure.lang.Namepace.importClasswhich takes a symbol and a type to map the symbol to.I am undecided on the actual name of the or whether it should be a macro or a regular function.
References
ClrTypeSpecto this code for accuracy. Also, there are performance improvements, such as the use ofReadOnlySpan<char>-- spans were not around when the inspirational Mono code was written.Beta Was this translation helpful? Give feedback.
All reactions