Lesson 1 of 8
Program and method context
What Scala structure must be in place before a method can call a local recursive helper?
The previous chapter explained why pure functions are easier to reason about. To write one in Scala, you first need a place to put it, and that place is an object. An object is a named, single-instance container for related definitions. You declare one with object MyProgram and indent its members to group them inside. Scala has no static keyword, so an object plays that role from Java. Declaring object MyProgram creates nothing while the program runs; it groups code under one name so other code can refer to it.
object MyProgram:
def abs(n: Int): Int =
if n < 0 then -n
else n
private def formatAbs(x: Int) =
val msg = "The absolute value of %d is %d."
msg.format(x, abs(x))
@main def printAbs: Unit =
println(formatAbs(-42))Inside the object you define methods with def, a named method definition. The line def abs(n: Int): Int declares a method named abs. The def keyword starts the declaration, abs is the name, n: Int says the parameter n has type Int, and the trailing : Int declares the result type. A type annotation is a written type attached to a name, such as n: Int or that trailing : Int on the method. The declaration def abs only describes the method; it does not run the method when the program reads that line. The body of abs is the expression if n < 0 then -n else n, which produces either -n or n.
To use abs you write a method call: the name followed by arguments in parentheses, like abs(-42). A method call evaluates to the method's result. Inside MyProgram you can call abs without any prefix. From outside, dot notation selects it, as in MyProgram.abs(-42), where MyProgram is the namespace, meaning it qualifies its members. Writing import MyProgram.abs brings that member into local scope so you can call abs directly. A member marked private, like formatAbs, is visible only inside its own object, so another object in your program cannot call it.
A runnable program also needs an entry point, the method where execution starts. In the code above, @main def printAbs: Unit = ... is that entry point. Scala also accepts def main(args: Array[String]): Unit or extends App as alternate entry-point forms. The form you pick says nothing about whether your functions are pure; only the method bodies decide that. A val or def can be a local definition when you nest it inside a method or block. That is what lets a method like factorial define and call its own small helper, exposing nothing as an object member.
def factorial(n: Int): Int =
def loop(i: Int, acc: Int): Int =
if i <= 0 then acc
else loop(i - 1, i * acc)
loop(n, 1)Notice that formatAbs calls msg.format(x, abs(x)). Here format is a method on the String named msg, and it replaces the two %d placeholders with x and the result of abs(x), respectively. Objects group methods, calls supply arguments, local definitions keep helpers close to the method that uses them, and a private helper stays inside its object.