Values

Abstract value declarations

Abstract value declarations are written by wrapping the name and the type with VAL(...):

import treehugger.forest._, definitions._, treehuggerDSL._

val tree = (VAL("foo", IntClass): Tree)
// tree: Tree = ValDef(
//   Modifiers(0L, TypeName(""), List()),
//   Typed(Ident(TermName("foo")), TypeTree()),
//   EmptyTree
// )

treeToString(tree)
// res0: String = "val foo: Int"

or in general:

VAL(sym|"x", typ|"C").tree

where sym is a symbol and typ is a type of the value. In the above code, sym|"x" denotes that either a symbol or String is accepted as the name of the value, and type|"C" denotes that either a type or String is accepted as the type of the value.

Calling tree method creates a Tree without a right-hand side (rhs) expression. Because there's an implicit conversion, forcing VAL(...) as a Tree automatically calls tree method.

Value definitions

Value definitions are written by appending right-hand side tree after := as follows:

val tree2 = VAL("foo", IntClass) := LIT(0)
// tree2: ValDef = ValDef(
//   Modifiers(0L, TypeName(""), List()),
//   Typed(Ident(TermName("foo")), TypeTree()),
//   Literal(Constant(0))
// )

treeToString(tree2)
// res1: String = "val foo: Int = 0"

Like Scala, the type annotation can be omitted when rhs is provided:

val tree3 = VAL("foo") := LIT(0)
// tree3: ValDef = ValDef(
//   Modifiers(0L, TypeName(""), List()),
//   Ident(TermName("foo")),
//   Literal(Constant(0))
// )

treeToString(tree3)
// res2: String = "val foo = 0"

In addition, a symbol could be used to define a value instead of using String names.

object sym {
  val foo = RootClass.newValue("foo")
}

val tree4 = VAL(sym.foo) := LIT(0)
// tree4: ValDef = ValDef(
//   Modifiers(0L, TypeName(""), List()),
//   Ident(TermName("foo")),
//   Literal(Constant(0))
// )

treeToString(tree4)
// res3: String = "val foo = 0"

For a larger code base, using symbols makes the code more readable.

The general form of constant value definitions are:

VAL(sym|"x", [typ]) := rhs

Lazy values

Lazy value declarations are written using LAZYVAL(...):

val tree5 = (LAZYVAL("foo", IntClass): Tree)
// tree5: Tree = ValDef(
//   Modifiers(2147483648L, TypeName(""), List()),
//   Typed(Ident(TermName("foo")), TypeTree()),
//   EmptyTree
// )

treeToString(tree5)
// res4: String = "lazy val foo: Int"

and lazy value definitions are written as:

val tree6 = LAZYVAL("foo", IntClass) := LIT(0)
// tree6: ValDef = ValDef(
//   Modifiers(2147483648L, TypeName(""), List()),
//   Typed(Ident(TermName("foo")), TypeTree()),
//   Literal(Constant(0))
// )

treeToString(tree6)
// res5: String = "lazy val foo: Int = 0"

Final values

Final value definitions are written by appending withFlags(...) after VAL(...):

val tree7 = VAL("foo", IntClass) withFlags(Flags.FINAL) := LIT(0)
// tree7: ValDef = ValDef(
//   Modifiers(32L, TypeName(""), List()),
//   Typed(Ident(TermName("foo")), TypeTree()),
//   Literal(Constant(0))
// )

treeToString(tree7)
// res6: String = "final val foo: Int = 0"

Pattern values

There is another form of value definitions called pattern values, but it will be discussed later.