Under certain circumstances, you can omit some parameters of method calls that are considered repetitive.
Those parameters are called Context Parameters because they are inferred by the compiler from the context surrounding the method call.
For instance, consider a program that sorts a list of addresses by two criteria: the city name and then street name.
val addresses: List[Address] = ...
addresses.sortBy(address => (address.city, address.street))
The sortBy
method takes a function that returns, for every address, the value to compare it with the other addresses.
In this case, we pass a function that returns a pair containing the city name and the street name.
Note that we only indicate what to compare, but not how to perform the comparison.
How does the sorting algorithm know how to compare pairs of String
?
Actually, the sortBy
method takes a second parameter—a context parameter—that is inferred by the compiler.
It does not appear in the above example because it is supplied by the compiler.
This second parameter implements the how to compare.
It is convenient to omit it because we know String
s are generally compared using the lexicographic order.
However, it is also possible to pass it explicitly:
addresses.sortBy(address => (address.city, address.street))(Ordering.Tuple2(Ordering.String, Ordering.String))
addresses.sortBy(address => (address.city, address.street))(using Ordering.Tuple2(Ordering.String, Ordering.String))
in Scala 3 using
in an argument list to sortBy
signals passing the context parameter explicitly, avoiding ambiguity.
In this case, the Ordering.Tuple2(Ordering.String, Ordering.String)
instance is exactly the one that is otherwise inferred by the compiler.
In other words both examples produce the same program.
Contextual Abstractions are used to avoid repetition of code. They help developers write pieces of code that are extensible and concise at the same time.
For more details, see the Contextual Abstractions chapter of this book, and also the Reference documentation.
Contributors to this page:
Contents
- Introduction
- Scala Features
- Why Scala 3?
- A Taste of Scala
- Hello, World!
- The REPL
- Variables and Data Types
- Control Structures
- Domain Modeling
- Methods
- First-Class Functions
- Singleton Objects
- Collections
- Contextual Abstractions
- Toplevel Definitions
- Summary
- A First Look at Types
- String Interpolation
- Control Structures
- Domain Modeling
- Tools
- OOP Modeling
- FP Modeling
- Methods
- Method Features
- Main Methods in Scala 3
- Summary
- Functions
- Anonymous Functions
- Function Variables
- Eta-Expansion
- Higher-Order Functions
- Write Your Own map Method
- Creating a Method That Returns a Function
- Summary
- Packaging and Imports
- Scala Collections
- Collections Types
- Collections Methods
- Summary
- Functional Programming
- What is Functional Programming?
- Immutable Values
- Pure Functions
- Functions Are Values
- Functional Error Handling
- Summary
- Types and the Type System
- Inferred Types
- Generics
- Intersection Types
- Union Types
- Algebraic Data Types
- Variance
- Opaque Types
- Structural Types
- Dependent Function Types
- Other Types
- Contextual Abstractions
- Extension Methods
- Context Parameters
- Context Bounds
- Given Imports
- Type Classes
- Multiversal Equality
- Implicit Conversions
- Summary
- Concurrency
- Scala Tools
- Building and Testing Scala Projects with sbt
- Worksheets
- Interacting with Java
- Scala for Java Developers
- Scala for JavaScript Developers
- Scala for Python Developers
- Where To Go Next