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.

  1. 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]"]
    
  2. 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.

  1. The type-reflect function inspired by clojure.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 Fijit type-inspect docstring for more information.
  2. 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 ThisTypes 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.