Functions in Scala
Learn how to define and use functions in Scala - methods, parameters, default arguments, recursion, higher-order functions, and closures with Docker-ready examples
Functions are where Scala’s dual nature really shines. As a language that unifies object-oriented and functional programming, Scala treats functions as both named blocks of behavior (methods, declared with def) and as honest-to-goodness values that can be stored in variables, passed as arguments, and returned from other functions. This is the heart of functional programming, and it’s available to you from the very first line of Scala you write.
In Java, a method is something attached to a class—it isn’t a value you can hand around. Scala keeps def methods but adds first-class function values on top, so x => x * 2 is an expression with a type (Int => Int) just like 42 has the type Int. This unlocks higher-order functions, closures, and the elegant collection operations (map, filter, reduce) that make Scala code so concise.
In this tutorial you’ll learn how to define methods, supply parameters and return values, use default and named arguments, write recursive functions, and—most importantly for a functional language—pass functions as arguments and return them as results.
Defining and Calling Functions
A Scala method is introduced with def, followed by a name, a parameter list with explicit types, an optional return type, and a body after =. Because Scala has powerful type inference, the return type can often be omitted—but it’s good practice (and required for recursive functions) to declare it.
Create a file named Functions.scala:
| |
This single file demonstrates the full progression: a plain method, a multi-line body, default and named arguments, recursion, a higher-order function, a closure, and first-class functions used with collections. Notice there is no return keyword anywhere—in Scala the value of the last expression is the result, which is why methods read like mathematical definitions.
Scope and Nested Functions
Scala is block-structured: a value or function is visible only within the block where it’s defined. Functions can be nested inside other functions, which is a clean way to hide helper logic that callers shouldn’t see. This example also highlights the distinction between a def method and a function value stored in a val.
Create a file named Scope.scala:
| |
The nested tax function keeps the calculation private to checkout, while still reaching outward to read taxRate—that outward reach is lexical scoping. The square value and the cube method behave identically when called, but only square can be dropped into a List and mapped over, because it’s a value.
Running with Docker
You can run both examples with the official Scala CLI image—no local Scala installation required.
| |
Expected Output
Running Functions.scala produces:
add(3, 4) = 7
Hello, Scala!
power(5) = 25
power(2, 10) = 1024
power(exponent = 3, base = 2) = 8
factorial(5) = 120
applyTwice(_ + 3, 10) = 16
triple(7) = 21
doubled = List(2, 4, 6, 8, 10)
evens = List(2, 4)
total = 15
Running Scope.scala produces:
checkout(100.0) = 110.0
square(4) = 16
cube(4) = 64
applied to 5: List(25, 6)
Key Concepts
defdefines methods — A method takes a typed parameter list and returns the value of its last expression; there is noreturnkeyword in idiomatic Scala.- Functions are first-class values — A lambda like
x => x * 2has the typeInt => Intand can be stored in aval, passed as an argument, or returned from another function. - Higher-order functions — Methods such as
applyTwiceaccept other functions as parameters, and collection methods likemap,filter, andreduceare higher-order functions you’ll use constantly. - Default and named arguments — Parameters can declare default values (
exponent: Int = 2), and callers can pass arguments by name in any order for clarity. - Recursion needs a return type — The compiler can’t infer the type of a recursive method, so functions like
factorialmust declare their result type explicitly. - Closures capture their environment — A function returned from
multiplierremembers thefactorvalue it closed over, even after the enclosing method has finished. - Lexical scope and nesting — Functions can be nested to hide helpers, and inner functions can read values from their enclosing scope while staying invisible to the outside.
Running Today
All examples can be run using Docker:
docker pull virtuslab/scala-cli:latest
Comments
Loading comments...
Leave a Comment