erp12.fijit.alpha.reflect
Interface into scala-reflect
and scala-compiler
.
WARNING: This is an experimental (alpha) namespace. It is published in order to facilitate community feedback in the overall design. Expect minor breaking changes in this namespace before a stable design is moved to the non-alpha namespace.
TypeTag and ClassTag
Scala provides an abstractions for inspecting types.
- The
ClassTag
type can provide information about a runtime class (post type-erasure). All information about type parameters is lost. - The
TypeTag
type can provide all type information that the Scala compiler has available at compile time. This includes all type parameter information.
You can read more about Scala type/class tags on their Scala documentation page.
Scala libraries will often expose abstractions that require a type-tag (or class-tag) argument that corresponds to the data type of another argument. In Scala, the type-tags are typically provided implicitly but in Clojure they must be provided explicitly.
Class-tag objects are created from Class objects.
(class-tag String)
; => #object[scala.reflect.ClassTag$GenericClassTag 0x7c172ce2 "java.lang.String"]
Fijit exposes two ways to create type-tags.
-
From a class object. This only works for types with no generic parameters.
(type-tag String) ; => #object[scala.reflect.api.TypeTags$TypeTagImpl 0x7c172ce2 "TypeTag[String]"] (type-tag scala.Product) ; => #object[scala.reflect.api.TypeTags$TypeTagImpl 0x1e85e9f0 "TypeTag[Product]"]
-
From a Scala
Type
object (see next section).(type-tag (scala-type scala.Option [Long])) ; => #object[scala.reflect.api.TypeTags$TypeTagImpl 0x35f97f3b "TypeTag[Option[Long]]"]
Creating a type-tag can be quite slow in many situations. By default, Fijit will memoize the creation of type-tags so that any repeated calls with the same type will be faster. In addition, the type-tags for most simple Scala types (scala.Int
, scala.Double
, etc.) are available provided as values in this namespace.
Scala Types
A Scala Type
object provides information about a type, including its:
- Members (methods, fields, type aliases, abstract types, nested classes, traits, etc.) either declared directly or inherited.
- Base types
- Erasure
- Conformance
- Equivalence
Fijit provides a function to create Scala type objects.
(scala-type java.lang.String)
; => #object[scala.reflect.internal.Types$ClassNoArgsTypeRef 0x29ed2fb4 "String"]
(scala-type scala.Option [Integer])
; => => #object[scala.reflect.internal.Types$ClassArgsTypeRef 0x1f8ceb "Option[Integer]"]
; Scala types can be parameterized with other Scala types
(scala-type scala.collection.Seq [(scala-type scala.Option [scala.Int])])
; => #object[scala.reflect.internal.Types$ClassArgsTypeRef 0x665c3f13 "scala.collection.Seq[Option[Int]]"]
Inspecting Types
Fijit provides 2 ways of inspecting Scala types.
- The
type-reflect
function inspired byclojure.reflect/type-reflect
. It returns all information about a type as data. Unlike Clojure’s reflect, the Fijit version allow you to inspect the members as seen by Scala. This means that some types will have class, trait, and type members according to the path-dependant types. Each member’s data includes a Scala symbol for use in further inspection. See the Fijittype-inspect
docstring for more information. - Functions corresponding to the
Type
methods found in Scala.
Scala Abstract Syntax Trees
Scala Tree
objects are the basis of Scala’s abstract syntax which is used to represent programs. See the official Scala docs on Trees for more information.
Fijit provides a suite of functions that each correspond to a different kind of node in the Scala AST. For example, the class-def
function is used to create a ClassDef
node.
Another way to create a Tree
is to call the parse
function and pass in a string containing valid Scala code.
Compilation
After constructing a Scala AST, it can be used in a variety of ways.
- The
compile
function will compile the tree. This will attempt to resolve all type variables, and type check the tree. - The
define
function will defines a top-level class, trait or module in a uniquely-named package and return a symbol that references the defined entity. - The
eval
function will compile and run a tree, returning the resulting value.
abstract-override?
(abstract-override? symb)
abstract?
(abstract? symb)
alternative
(alternative trees)
annotated
(annotated annot arg)
any-cls-tag
A ClassTag
for the scala.Any
class.
any-ref-cls-tag
A ClassTag
for the scala.AnyRef
class.
any-ref-tt
A TypeTag
for the scala.AnyRef
type.
any-tt
A TypeTag
for the scala.Any
type.
any-val-cls-tag
A ClassTag
for the scala.AnyVal
class.
any-val-tt
A TypeTag
for the scala.AnyVal
type.
applied-type-tree
(applied-type-tree tpt args)
apply
(apply func args)
as-class
(as-class symb)
The symbol cast to a ClassSymbol
representing a class or trait.
as-method
(as-method symb)
The symbol cast to a MethodSymbol
.
as-module
(as-module symb)
The symbol cast to a ModuleSymbol
defined by an object definition.
as-seen-from
(as-seen-from typ pre class)
This type as seen from prefix pre
and class class
.
This means: Replace all ThisTypes
of class
or one of its subclasses by pre
and instantiate all parameters by arguments of pre
. Proceed analogously for ThisType
s referring to outer classes.
For examples, see [the scala docs](https://www.scala-lang.org/api/2.13.6/scala-reflect/scala/reflect/api/Types$TypeApi.html#asSeenFrom(pre:Types.this.Type,clazz:Types.this.Symbol):Types.this.Type).
as-term
(as-term symb)
The symbol cast to a TermSymbol
.
as-type
(as-type symb)
The symbol cast to a TypeSymbol
.
assign
(assign lhs rhs)
base-classes
(base-classes typ)
The list of all base classes of this type (including its own type-symbol) in linearization order, starting with the class itself and ending in class Any.
base-type
(base-type typ class)
The least type instance of given class which is a super-type of typ
.
For examples, see [the scala docs](https://www.scala-lang.org/api/2.13.6/scala-reflect/scala/reflect/api/Types$TypeApi.html#baseType(clazz:Types.this.Symbol):Types.this.Type).
bind
(bind name body)
block
(block {:keys [stats expr], :or {stats []}})
boolean-cls-tag
A ClassTag
for the scala.Boolean
class.
boolean-tt
A TypeTag
for the scala.Boolean
type.
byte-cls-tag
A ClassTag
for the scala.Byte
class.
byte-tt
A TypeTag
for the scala.Byte
type.
can-have-attrs?
(can-have-attrs? tree)
case-def
(case-def {:keys [pat guard body]})
char-cls-tag
A ClassTag
for the scala.Char
class.
char-tt
A TypeTag
for the scala.Char
type.
children
(children tree)
class-def
(class-def {:keys [mods name tparams impl], :or {mods (modifiers), tparams [], impl (template {:body [default-constructor]})}})
class-loader
The current thread’s context class loader.
class-tag
(class-tag cls)
class?
(class? symb)
companion
(companion typ)
Type signature of the companion of the underlying class symbol. NoType
if the underlying symbol is not a class symbol, or if it doesn’t have a companion.
compile
(compile tree)
Compiles a tree.
compound-type-tree
(compound-type-tree templ)
conforms?
(conforms? sub-type super-type)
Tests if sub-type
conforms to super-type
.
constant
(constant value)
constructor
The CONSTRUCTOR
term name.
constructor?
(constructor? symb)
dealias
(dealias typ)
Expands type aliases arising from type members.
decl
(decl typ name)
The defined or declared members with name name in this type; an OverloadedSymbol
if several exist, NoSymbol
if none exist.
Alternatives of overloaded symbol appear in the order they are declared.
decls
(decls typ)
A list containing directly declared members of this type in sorted order.
decoded-name
(decoded-name nm)
The decoded name.
Example:
(decoded-name (term-name "$bang$eq")) ; => (term-name "!=")
def-def
(def-def {:keys [mods name tparams vparamss tpt rhs], :or {mods (modifiers), tparams [], vparamss [], tpt (type-tree)}})
def?
(def? tree)
default-constructor
define
(define tree)
Defines a top-level class, trait, or module, putting it into a uniquely-named package and returning a symbol that references the defined entity. For a ClassDef
, a ClassSymbol
is returned, and for a ModuleDef
, a ModuleSymbol
is returned (not a module class, but a module itself).
This method can be used to generate definitions that will later be re-used by subsequent calls to compile
, define
or eval
. To refer to the generated definition in a tree, use its symbol.
double-cls-tag
A ClassTag
for the scala.Double
class.
double-tt
A TypeTag
for the scala.Double
type.
duplicate
(duplicate tree)
empty-package-name
The EMPTY_PACKAGE_NAME
term name.
empty-term-name
The EMPTY
term name.
empty-tree
The empty tree.
empty-type-name
The EMPTY
type name.
empty?
(empty? tree)
encoded-name
(encoded-name nm)
The encoded name.
Example:
(encoded-name (term-name "!=")) ; => (term-name "$bang$eq")
equivalent?
(equivalent? type-1 type-2)
Tests if type-1
is equivalent to type-2
. Returns true or false.
erasure
(erasure typ)
error-term-name
The ERROR
term name.
error-type-name
The ERROR
type name.
eval
(eval tree)
Compiles and runs a tree. Is equivalent to (.apply (compile tree))
existential-type-tree
(existential-type-tree tpt where-clauses)
final?
(final? symb)
float-cls-tag
A ClassTag
for the scala.Float
class.
float-tt
A TypeTag
for the scala.Float
type.
full-name
(full-name symb)
The encoded full path name of the symbol, where outer names and inner names are separated by periods.
full-type-pr
(full-type-pr typ)
Create a string containing the fully qualify and parameterized type name.
Example:
(full-type-pr (scala-type scala.Option [String])) ; => "scala.Option[java.lang.String]"
function
(function vparams body)
ident
(ident name)
if
(if cond thenp elsep)
implementation-artifact?
(implementation-artifact? symb)
implicit?
(implicit? symb)
import
(import expr selectors)
import-selector
(import-selector name name-pos rename rename-pos)
info
(info symb)
int-cls-tag
A ClassTag
for the scala.Int
class.
int-tt
A TypeTag
for the scala.Int
type.
java-annotation?
(java-annotation? symb)
java-enum?
(java-enum? symb)
java?
(java? symb)
label-def
(label-def name params rhs)
literal
(literal value)
local-suffix-string
The LOCAL_SUFFIX_STRING
term name.
long-cls-tag
A ClassTag
for the scala.Long
class.
long-tt
A TypeTag
for the scala.Long
type.
macro?
(macro? symb)
match
(match selector cases)
member
(member typ name)
The member with given name, either directly declared or inherited, an OverloadedSymbol
if several exist,NoSymbol
if none exist.
members
(members typ)
A list containing all members of this type (directly declared or inherited) in sorted order.
method?
(method? symb)
mirror
The reflective mirror of the universe. Used for loading Symbols by name, and as an entry point into invoker mirrors.
modifiers
(modifiers)
(modifiers flags private-within annotations)
module-def
(module-def {:keys [mods name impl], :or {mods (modifiers), impl (template {})}})
module?
(module? symb)
name
(name symb)
The name of the symbol as a member of the Name
type. Can be either TermName
or TypeName
depending on whether the given symbol is a TermSymbol
or a TypeSymbol
.
named-arg
(named-arg lhs rhs)
new
(new tpt)
no-prefix
A constant used as a special value denoting the empty prefix in a path dependent type.
no-self-type
An empty deferred value definition. This is used as a placeholder in the self
parameter Template if there is no definition of a self value of self type.
no-type
A constant used as a special value to indicate that no meaningful type exists.
non-empty?
(non-empty? tree)
nothing-cls-tag
A ClassTag
for scala.Noting
.
nothing-tt
A TypeTag
for the scala.Nothing
type.
null-cls-tag
A ClassTag
for the scala.Null
.
null-tt
A TypeTag
for the scala.Null
type.
object-cls-tag
A ClassTag
for the java.lang.Object
class.
object-tt
A TypeTag
for the java.lang.Object
type.
owner
(owner symb)
The owner of this symbol. This is the symbol that directly contains the current symbol’s definition. The NoSymbol
symbol does not have an owner, and calling this method on one causes an internal error.
The owner of the Scala RootClass
and RootPackage
is NoSymbol
. Every other symbol has a chain of owners that ends in RootClass
.
package-def
(package-def {:keys [pid stats]})
package-term-name
The PACKAGE
term name.
package-type-name
The PACKAGE
type name.
package?
(package? symb)
param-lists
(param-lists typ)
For a method or polymorphic type, a list of its value parameter sections. An empty list of lists for all other types.
parameter?
(parameter? symb)
parse
(parse code)
Parses Scala code into a Scala AST.
pos
(pos tree)
pr-code
(pr-code tree {:keys [types? ids? owners? positions? root-pkg?], :or {types? nil, ids? nil, owners? nil, positions? nil, root-pkg? false}})
pr-raw
(pr-raw tree {:keys [types? ids? owners? kinds? mirrors? positions?], :or {types? nil, ids? nil, owners? nil, kinds? nil, mirrors? nil, positions? nil}})
private-this?
(private-this? symb)
private?
(private? symb)
protected-this?
(protected-this? symb)
protected?
(protected? symb)
public?
(public? symb)
reflect
(reflect obj)
Creates a reflective mirror for the given object.
reflect-class
(reflect-class symb)
Reflects against a static class symbol and returns a mirror that can be used to create instances of the class, inspect its companion object or perform further reflections.
reflect-constructor
(reflect-constructor cm ctor)
Reflects against a constructor symbol and returns a mirror that can be used to invoke it and construct instances of the mirror’s symbols.
reflect-field
(reflect-field im symb)
Reflects against a field symbol and returns a mirror that can be used to get and, if appropriate, set the value of the field.
return
(return expr)
root-pkg
The ROOTPKG
term name.
scala-symbol?
(scala-symbol? s)
Checks if s
is a Scala reflect Symbol
.
scala-type
(scala-type cls)
(scala-type cls type-args)
Creates a Scala Type
from the class (cls
) and type-args
(if required).
scala-type?
(scala-type? x)
Returns true
if x
is a Scala Type
object. false
otherwise.
select
(select qualifier name)
select-from-type-tree
(select-from-type-tree qualifier name)
short-cls-tag
A ClassTag
for the scala.Short
class.
short-tt
A TypeTag
for the scala.Short
type.
singleton-type-tree
(singleton-type-tree ref)
specialized?
(specialized? symb)
star
(star elem)
static?
(static? symb)
structurally-equal?
(structurally-equal? tree-a tree-b)
super
(super qual mix)
symbol
(symbol tree)
synthetic?
(synthetic? symb)
takes-type-args?
(takes-type-args? typ)
Return true if the type constructor is missing its type arguments, and false otherwise.
template
(template {:keys [parents self body], :or {parents [], self no-self-type, body []}})
term-name
(term-name s)
term-name?
(term-name? nm)
Checks whether nm
is a term name.
term-symbol
(term-symbol typ)
The term symbol associated with the type Note that the symbol of the normalized type is returned
term?
(term? symb-or-tree)
Checks if the given Symbol
or Tree
is denoting a term.
this
(this qual)
throw
(throw expr)
to-term-name
(to-term-name nm)
Returns a term name that wraps the same string as the name nm
.
to-type
(to-type symb)
The “type” of the symbol.
The type of a term symbol is its usual type.
For type symbols, this is different than the info
function because returns a typeRef
to the type symbol. info
returns the type information of the type symbol.
to-type-name
(to-type-name nm)
Returns a type name that wraps the same string as the name nm
.
toolbox
A toolbox that can be used to invoke the scala compiler.
tpe
(tpe tree)
try
(try block catches finalizer)
type->tag
(type->tag typ & {:keys [memoize?], :or {memoize? true}})
Create a TypeTag
for the given Type
.
Requires expensive reflection operations. By default, results will be memoized. To disable memoization, use :memoize? false
.
type->tree
(type->tree typ)
type-apply
(type-apply func args)
type-args
(type-args typ)
A list of type arguments ingrained in this type reference. Depending on your use case you might or might not want to call dealias
first.
Example:
(type-args (scala-type scala.collection.immutable.List [scala.Int])) ;; => [scala.Int]
type-bounds-tree
(type-bounds-tree lower higher)
type-constructor
(type-constructor typ)
Returns the corresponding type constructor (e.g. List
for List[T]
or List[String]
)
type-def
(type-def {:keys [mods name tparams rhs], :or {mods (modifiers), tparams [], rhs empty-tree}})
type-name
(type-name s)
type-name?
(type-name? nm)
Checks whether nm
is a type name.
type-params
(type-params typ)
For a polymorphic type, its type parameters. An empty list for all other types.
type-reflect
(type-reflect typ & {:keys [ancestors?], :or {ancestors? false}})
Reflect on a Scala type, returning a map with :bases
, :flags
, and :members
. Inspired by clojure.reflect/type-reflect
except for Scala types. Names are represented as Scala reflect Symbol
objects rather than Clojure symbols, and parameters are represented as symbols rather than just their type.
:bases a set of symbols of the type’s bases :flags a set of keywords naming the boolean attributes of the type. :members a set of the type’s members. Each member is a map and can be a constructor, method, field, or path dependant types (ie. nested class).
Keys common to all members: :name A Clojure symbol denoting the member’s name :symbol The Scala Symbol of the member :declaring-class Symbol of the declarer :flags keyword naming boolean attributes of the member
Keys specific to constructors and methods: :parameters vector of parameter symbols. :return-type return type symbol
Keys specific to fields: :type type name
There are no additional symbols for nested class
Options:
:ancestors? If true, add all ancestor members to :members
.
type-symbol
(type-symbol typ)
The type symbol associated with the type.
type-tag
(type-tag cls-or-type & {:keys [memoize?], :or {memoize? true}})
Create a TypeTag
from the given class or Scala
type.
type-tree
(type-tree)
type?
(type? symb-or-tree)
Checks if the given Symbol
or Tree
is denoting a type.
typed
(typed expr tpt)
un-apply
(un-apply fun args)
unit-cls-tag
A ClassTag
for the scala.Unit
class.
unit-tt
A TypeTag
for the scala.Unit
type.
universe
The runtime reflection universe.
val-def
(val-def {:keys [mods name tpt rhs], :or {mods (modifiers), tpt (type-tree)}})
weak-conforms?
(weak-conforms? sub-type super-type)
Tests if sub-type
weakly conforms to super-type
. Returns true or false.
A type weakly conforms if either:
- it conform in terms of `(conforms sub-type super-type)
- both are primitive number types that conform according to “weak conformance”.
Example:
(weak-conforms? (scala-type scala.Int) (scala-type scala.Long)) ; => true
widen
(widen typ)
If this is a singleton type, widen it to its nearest underlying non-singleton base type by applying one or more underlying
dereferences. If this is not a singleton type, returns this type itself.
wildcard-start
The WILDCARD_STAR
type name.
wildcard-term-name
The WILDCARD
term name.
wildcard-type-name
The WILDCARD
type name.