Open In App

Scala Context Bound

Last Updated : 23 Feb, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Context bound is a shorthand for expressing the common pattern of a context parameter that depends on a type parameter. Type parameters are placeholders for types, these are used to write type generic code, and these are declared in []. Context parameters are implicit type parameters, they help enrich the Type parameter. They are used to write generic polymorphic code and help encode types into runtime objects and can enable the creation of type classes that abstract behavior from functions. In situations where a generic type argument is passed in a function, there is a requirement for some implicit argument to provide essential information in relation to it, context bounds are a shorthand for expressing this in the function signature. Context bounds allow you to express constraints on type parameters and make it easier to write generic code.

Two new keywords have been introduced in place of implicit in Scala3, – given and using, the implicit keyword is supported in Scala3 but will be removed in later releases. 

A context bound [: ord] on a type parameter [T] of a method indicates a context parameter [using Ord[T]], the usage of context-bound in Scala is to reduce the code redundancy involved in passing implicit arguments (using and given clause). In many situations, the name of a context parameter doesn’t have to be mentioned explicitly since it’s only used by the compiler in synthesized arguments for other context parameters. This generic coding style is referred to as Ad-Hoc Polymorphism. Context bounds help clean the code and reduce verbosity.

Syntax:

// regular function signature

def func[T](implicit x: Class_manifest[T]) {}

// function signature using context bound

def func[T : Class_manifest] {}

In the above code, we have defined a context-bound Class_manifest  (some type class) on a type parameter ‘T’. There must be an implicit value of Class_manifest[T] in the scope for type ‘T’. The Class_manifest provides variable-specific details necessary for performing the computational tasks on variables of type ‘T’ inside the function. Function func can be used with any type ‘T’, and takes no arguments.

Class_manifest has been deprecated since 2.10, it was a type class for generics, in this example it can be replaced with Ordered or Numeric.

def maximum[T: Ord](xs: List[T]): T = xs.reduceLeft(max)

This is an example from the Scala-lang documentation page for context bounds. The maximum function takes a list of type ‘T’ and returns a type ‘T’ value, [: Ord] is the bound on ‘T’, it encapsulates the necessary runtime information required for handling the values of type ‘T’.

Example 1: Below is the Scala program to implement context-bound.

Scala




import scala.math.Ordering
  
object CompareExample {
  def compare[T: Ordering](var1: T, var2: T): T = {
    if (implicitly[Ordering[T]].compare(var1, var2) > 0) var1 else var2
  }
  
  def main(args: Array[String]): Unit = {
    println(compare(1, 2))
    println(compare("a", "b"))
    println(compare(1.0, 2.0))
  }
}


Output

2
b
2.0

Explanation: The function compare can be used with any type ‘T’ that has a value for Ordering[T] in the scope. The function takes two variables of type ‘T’, var2 is returned if the value of the statement inside it is 1 i.e. var1 has a greater value than var2. The statement inside the if can be decomposed to:

implicit ival = implicitly[Ordering[T]]

if (ival.compare(var1, var2) > 0) var1 else var2

Example 2: Below is the Scala program to implement context-bound.

Scala




trait Vehicle {
  def name: String
  def capacity: Int
}
  
case class Bike(name: String) extends Vehicle {
  val capacity = 2
}
  
case class Car(name: String) extends Vehicle {
  val capacity = 4
}
  
object SeatCapacityExample {
  def seat_capacity[T <: Vehicle](veh: T) = {
    s"There are ${veh.capacity - 1} passenger seat(s) in ${veh.name}"
  }
  
  def main(args: Array[String]): Unit = {
    println(seat_capacity(Bike("Pulsar")))
    println(seat_capacity(Car("Baleno")))
  }
}


Output

There are 1 passenger seat(s) in Pulsar
There are 3 passenger seat(s) in Baleno

Explanation: The example is self-explanatory, there is a trait vehicle that is extended by Bike and Car classes. The seat_capacity function has a context bound of [: Vehicle] on T, it takes one argument. The context bound on veh ensures that veh should be an instance of the ‘Vehicle’ trait. The context bound is used to obtain an implicit Vehicle[veh] value to access the name and capacity of the trait.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads