Movatterモバイル変換


[0]ホーム

URL:


Quasiquotes

Type details

    Language
      This doc page is specific to features shipped in Scala 2, which have either been removed in Scala 3 or replaced by an alternative. Unless otherwise stated, all the code examples in this page assume you are using Scala 2.

      Denys ShabalinEXPERIMENTAL

      Empty Type

      The empty type (tq"") is a canonical way to say that the type at given location isn’t given by the user and should be inferred by the compiler:

      1. Method with unknown return type
      2. Val or Var with unknown type
      3. Anonymous function with unknown argument type

      Type Identifier

      Similarly toterm identifiers one can construct a type identifier based on a name:

      scala> val name = TypeName("Foo")name: universe.TypeName = Fooscala> val foo = tq"$name"foo: universe.Ident = Foo

      And deconstruct it back throughunlifting:

      scala> val tq"${name: TypeName}" = tq"Foo"name: universe.TypeName = Foo

      It is recommended to always ascribe the name asTypeName when you work with type identifiers. A non-ascribed pattern is equivalent to a pattern variable binding.

      Singleton Type

      A singleton type is a way to express a type of term definition that is being referenced:

      scala> val singleton = tq"foo.bar.type".srsingleton: String = SingletonTypeTree(Select(Ident(TermName("foo")), TermName("bar")))scala> val tq"$ref.type" = tq"foo.bar.type"ref: universe.Tree = foo.bar

      Type Projection

      Type projection is a fundamental way to select types as members of other types:

      scala> val proj = tq"Foo#Bar"proj: universe.SelectFromTypeTree = Foo#Barscala> val tq"$foo#$bar" = projfoo: universe.Tree = Foobar: universe.TypeName = Bar

      Similarly to identifiers, it's recommended to always ascribe the name asTypeName. Non-ascribed matching behaviour might change in the future.

      As a convenience one can also select type members of terms:

      scala> val int = tq"scala.Int"int: universe.Select = scala.Intscala> val tq"scala.$name" = intname: universe.TypeName = Int

      But semantically, such selections are just a shortcut for a combination of singleton types and type projections:

      scala> val projected = tq"scala.type#Int"projected: universe.SelectFromTypeTree = scala.type#Int

      Lastly andsimilarly to expressions one can select members throughsuper andthis:

      scala> val superbar = tq"super.Bar"superbar: universe.Select = super.Barscala> val tq"$pre.super[$parent].$field" = superbarpre: universe.TypeName =parent: universe.TypeName =field: universe.Name = Barscala> val thisfoo = tq"this.Foo"thisfoo: universe.Select = this.Fooscala> val tq"this.${tpname: TypeName}" = thisfootpname: universe.TypeName = Foo

      Applied Type

      Instantiations of parameterized types can be expressed with the help of applied types (type-level equivalent of type application):

      scala> val applied = tq"Foo[A, B]"applied: universe.Tree = Foo[A, B]scala> val tq"Foo[..$targs]" = appliedtargs: List[universe.Tree] = List(A, B)

      Deconstruction of non-applied types will causetargs to be extracted as an empty list:

      scala> val tq"Foo[..$targs]" = tq"Foo"targs: List[universe.Tree] = List()

      Annotated Type

      Similarly to expressions, types can be annotated:

      scala> val annotated = tq"T @Fooable"annotated: universe.Annotated = T @Fooablescala> val tq"$tpt @$annot" = annotatedtpt: universe.Tree = Tannot: universe.Tree = Fooable

      Compound Type

      A compound type lets users express a combination of a number of types with an optional refined member list:

      scala> val compound = tq"A with B with C"compound: universe.CompoundTypeTree = A with B with Cscala> val tq"..$parents { ..$defns }" = compoundparents: List[universe.Tree] = List(A, B, C)defns: List[universe.Tree] = List()

      Braces after parents are required to signal that this type is a compound type, even if there are no refinements, and we just want to extract a sequence of types combined with thewith keyword.

      On the other side of the spectrum are pure refinements without explicit parents (a.k.a. structural types):

      scala> val structural = tq"{ val x: Int; val y: Int }"structural: universe.CompoundTypeTree =scala.AnyRef {  val x: Int;  val y: Int}scala> val tq"{ ..$defns }" = structuraldefns: List[universe.Tree] = List(val x: Int, val y: Int)

      Here we can see that AnyRef is a parent that is inserted implicitly if we don’t provide any.

      Existential Type

      Existential types consist of a type tree and a list of definitions:

      scala> val tq"$tpt forSome { ..$defns }" = tq"List[T] forSome { type T }"tpt: universe.Tree = List[T]defns: List[universe.MemberDef] = List(type T)

      Alternatively there is also an underscore notation:

      scala> val tq"$tpt forSome { ..$defns }" = tq"List[_]"tpt: universe.Tree = List[_$1]defns: List[universe.MemberDef] = List(<synthetic> type _$1)

      Tuple Type

      Similar to expressions, tuple types are just syntactic sugar overTupleN classes:

      scala> val tup2 = tq"(A, B)"tup2: universe.Tree = scala.Tuple2[A, B]scala> val tq"(..$tpts)" = tup2tpts: List[universe.Tree] = List(A, B)

      Analogously theUnit type is considered to be a nullary tuple:

      scala> val tq"(..$tpts)" = tq"_root_.scala.Unit"tpts: List[universe.Tree] = List()

      It is important to mention that pattern matching a reference toUnit is limited to either fully the qualified path or a reference that contains symbols. (seehygiene)

      Function Type

      Similar to tuples, function types are syntactic sugar overFunctionN classes:

      scala> val funtype = tq"(A, B) => C"funtype: universe.Tree = _root_.scala.Function2[A, B, C]scala> val tq"..$foo => $bar" = funtypefoo: List[universe.Tree] = List(A, B)bar: universe.Tree = C

      Contributors to this page:

      Contents


      [8]ページ先頭

      ©2009-2025 Movatter.jp