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.