-
Notifications
You must be signed in to change notification settings - Fork 157
Specifying types
Clojure uses symbols to name types in two ways:
- a package-qualified symbol (one containing periods internally) is taken to name the Java class with the same character sequence
- a namespace may contain a mapping from a symbol to a Java class, via
import
Resolving a symbol is the process of determining the value of a symbol during evalution.
Identifying types via symbol names works reasonably well for Java because package-qualified class names are lexically compatible with symbols.
Not so for the CLR. Fully-qualified type names can contain an assembly identifier, which involves spaces and commas. Thus, fully-qualified type names cannot be represented as symbols. Generic type names include angle brackets and backquotes, and thus also are not representable as symbols. In fact, CLR typenames can contain arbitrary characters. Backslashes can escape characters that do have special meaning in the typename syntax (comma, plus, ampersand, asterisk, left and right square bracket, left and right angle bracket, backslash).
ClojureCLR extends the reader syntax for symbols to accommodate CLR type name by the mechanism of vertical bar escaping.
Vertical bars are used in pairs to surround the name (or part of the name) of a symbol that has any special characters (from the standard Clojure symbolb viewpoint) in it. It is roughly equivalent to escaping every character in the surrounded fragment. For example, |A(B)|
, A|(|B|)|
, and A|(B)|
all mean the symbol whose name consists of the four characters A, (, B, and ).
|anything except a vertical bar...<>@#$@#$#$|
To include a vertical bar in a symbol name that is |-escaped, use a doubled vertical bar.
|This has a vertical bar in the name ... || ...<>@#$@#$#$|
There is a special interaction of |-escaping with / used to separate namespace from name. Any / appearing |-escaped does not count as a namespace/name separator. Thus,
(namespace 'ab|cd/ef|gh) ;=> nil
(name 'ab|cd/ef|gh) ;=> "abcd/efgh"
(namespace 'ab/cd|ef/gh|ij) ;=> "ab"
(name 'ab/cd|ef/gh|ij) => "cdef/ghij" ;=>
With this mechanism can we make a symbol referring to a type such as:
(|com.myco.mytype+nested, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b14a123334343434|/DoSomething x y)
or
(reify
|AnInterface`2[System.Int32,System.String]|
(m1 [x] ...)
I2
(m2 [x] ...))
Special note should be made of the proper way to refer to generic types (instantiated or not).
|System.Collections.Generic.IList`1[System.Int32]|
This is the official CLR way of referring to the type that would be referred to in C# (with an import) by IList<int>
. We do not implement C# or Visual Basic lexical conventions for type names. The existence of characters such as the backquote and square brackets force some type of escaping mechanism.
Note: |-escaping defined in ClojureCLR differs from the similar mechanism in CommonLisp in one significant way:
- CommonLisp allows a literal vertical bar in a symbol name with backslash-escaping: abc|123 has name “abc|123”. ClojureCLR uses a doubled vertical bar, and only within an escaped symbol name. We would write |abc||123| for the symbol with name “abc|123”.
I have not yet implemented print-readably
for symbols with bad characters. This is straightforward and should be done.