README.adoc
May 13, 2025 ยท View on GitHub
:testdir: src/test/scala/io/github/gaeljw/typetrees
= TypeTrees :toc:
[link=https://search.maven.org/artifact/io.github.gaeljw/typetrees_3] image::https://img.shields.io/maven-central/v/io.github.gaeljw/typetrees_3.svg[] image::https://github.com/gaeljw/typetrees/workflows/Scala%20CI/badge.svg[]
WARNING: (2025-05) This library served as a prototype and should not used in any real project. It is unmaintained. More robust projects (or even vanilla Scala depending on the context) exist to achieve the same.
This library is intended to solve the use case of getting the erased types of a (generic) parameter.
Something which was possible in Scala 2 with +TypeTag+
and is now a bit more complex with Scala 3.
== Get it
.sbt [source]
libraryDependencies += "io.github.gaeljw" %% "typetrees" % typetreesVersion
.Maven [source,xml]
== Usage
Two implementations are available:
- using implicits (
givens) - using macro
|=== |Implementation |Pros ๐ |Cons ๐
|Implicits
|Easier
|Only supports up to 4 generic parameters (i.e. a type T[A, B, C, D])
|Macro
|Can handle all cases
|Requires to be used in inline method
|===
=== Using implicits
[source,scala]
import io.github.gaeljw.typetrees.TypeTree import io.github.gaeljw.typetrees.TypeTreeTag
import scala.reflect.ClassTag
val tag: TypeTreeTag = summon[TypeTree[T]].tag // <1>
val classTag: ClassTag[] = tag.self // <2> val actualClass: Class[] = classTag.runtimeClass
val typeParameters: List[TypeTreeTag] = tag.args // <3>
<1> Get a +TypeTreeTag+ for a generic type +T+
<2> Get a +ClassTag+ for this type
<3> Get a +TypeTreeTag+ for each type parameters if any
==== Examples
The main usage is as follows:
[source,scala]
def someGenericMethod[T](t: T)(using typeTree: TypeTree[T]): String = { val tag: TypeTreeTag = typeTree.tag s"I have been called with a parameter of type $tag" }
someGenericMethod(MapString, Int) // Gives: I have been called with a parameter of type TypeTreeTag(scala.collection.immutable.Map,List(TypeTreeTag(java.lang.String,List()), TypeTreeTag(Int,List())))
Or with context bounds:
[source,scala]
def someGenericMethod[T : TypeTree](t: T): String = { val tag: TypeTreeTag = summon[TypeTree[T]].tag s"I have been called with a parameter of type $tag" }
You can find more examples in our link:{testdir}/TestBehaviorImplicits.scala[tests].
=== Using macro
[source,scala]
import io.github.gaeljw.typetrees.TypeTreeTag import io.github.gaeljw.typetrees.TypeTreeTagMacros.typeTreeTag
import scala.reflect.ClassTag
val tag: TypeTreeTag = typeTreeTag[T] // <1>
val classTag: ClassTag[] = tag.self // <2> val actualClass: Class[] = classTag.runtimeClass
val typeParameters: List[TypeTreeTag] = tag.args // <3>
<1> Get a +TypeTreeTag+ for a generic type +T+
<2> Get a +ClassTag+ for this type
<3> Get a +TypeTreeTag+ for each type parameters if any
==== Examples
The main usage is as follows, within a generic +inline+ method:
[source,scala]
inline def someGenericMethod[T](t: T): String = { val tag: TypeTreeTag = typeTreeTag[T] s"I have been called with a parameter of type $tag" }
someGenericMethod(MapString, Int) // Gives: I have been called with a parameter of type TypeTreeTag(scala.collection.immutable.Map,List(TypeTreeTag(java.lang.String,List()), TypeTreeTag(Int,List())))
Or:
[source,scala]
inline def someGenericMapMethod[T <: Map[,]](map: T): String = { val mapTag: TypeTreeTag = typeTreeTag[T] val keyTag: TypeTreeTag = mapTag.args(0) val valueTag: TypeTreeTag = mapTag.args(1) s"I have been called with a Map where key is of type valueTag" }
It can also be applied to non generic types: in such case it doesn't need to be part of a +inline def+ but you probably can just use regular ClassTag then.
You can find more examples in our link:{testdir}/TestBehaviorMacro.scala[tests].