You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Mar 27, 2025. It is now read-only.
implicits enables overcoming common phenomena in Functional Programming:
Writing out complex instances in full can be very cumbersome
Functions lead to long argument lists, which render code less readable and feels like boilerplate
The killer application for implicits are so-called Type classes
Note that using implicits to pass in parameters implicitly is powerful but can also be quite confusing.
Caveat: implicits may make things a bit to easy to ignore. A good example of this is the availability of a global execution context in the Scala Runtime environment
Contextual Abstractions - II
Scala's implicits provide a solution to this:
Defining a value of some type marked as implicit
Adding a separate so-called implicit parameter list to a function
When such a function is called, the compiler will look-up the *implicit value *in scope (applying various scoping rules) and pass it to the function
In case no implicit value is found, the code will not compile
Instead of typing out complex instances in full, contextual abstractions allow one to "summon" such instances based on their type
Contextual Abstractions - III
Implicits turned out to be extremely useful for other reasons than the one quoted on the previous slide
Enables the implementation of Scala Type Classes
Dependency injection
So-called implicit conversions were touted for some time and misused by many, leading to code that was very difficult to understand
Scala 2's implicits confused many Scala users because of the multiple meanings of the implicit keyword
Used to define implicit values
Used to mark a function's parameter list as implicit
As a modifier on a class definition as the basis to define extension methods
The keyword implicitly to look-up an instance of an implicit value
"By design", in Scala 2, a function can have only one implicit parameter list
Contextual Abstractions - IV
Scala 3 redesigns the contextual abstractions to eliminate the aforementioned problems
The following contextual abstractions are defined in Scala 3:
given instances: used to define "canonical" values in some context
using clauses & summon-ing given instances
Context bounds
given imports
Extension methods
Multiversal equality
Context Functions, implicit conversions, type class derivation (not discussed in this course)
Let's start with the new extension methods syntax
Extension Methods
Extension methods are a great way to add functionality to a type after it's been defined.
They are a simpler alternative to implicit classes for the 'Enrich my library' pattern
We can define operators with the help of extension methods
extension (a: String)
def< (b: String):Boolean= a.compareTo(b) <0extension (a: Int)
def+: (b: List[Int]) = a :: b
println("abc"<"pqr") // truevallst=1+:List(2,3,4) // val lst: List[Int] = List(1, 2, 3, 4)
Extension Methods - Applicability - I
Four possible conditions may make an extension method applicable.
Let's have a look at each of them:
It is visible under a simple name, by being defined inherited or imported in a scope enclosing the application
traitIntOps:extension (i: Int) defisZero:Boolean= i ==0extension (i: Int) defsafeMod(x: Int):Option[Int] =// extension method defined in same scope IntOpsif x.isZero thenNoneelseSome(i % x)
objectIntOpsExextendsIntOps:extension (i: Int) defsafeDiv(x: Int):Option[Int] =// extension method brought into scope via inheritance from IntOpsif x.isZero thenNoneelseSome(i / x)
objectSafeDiv:importIntOpsEx._// brings safeDiv and safeMod into scopeextension (i: Int) defdivide(d: Int) :Option[(Int, Int)] =
(i.safeDiv(d), i.safeMod(d)) matchcase (Some(d), Some(r)) =>Some((d, r))
case _ =>NoneimportSafeDiv.*vald1=25.divide(3) // Some((8,1))vald2=25.divide(0) // None
Extension Methods - Applicability - II
It is a member of some given instance at the point of the application
traitIntOps:extension (i: Int)
defisZero:Boolean= i ==0defsafeMod(x: Int):Option[Int] =// extension method defined in same scope IntOpsif x.isZero thenNoneelseSome(i % x)
givenIntOps=newIntOps{} // IntOps is now a given in this scope20.safeMod(3) // Some(2)20.safeMod(0) // None
Extension Methods - Applicability - III
The reference is of the form r.m and the extension method is defined in the implicit scope of the type of r.
The reference is of the form r.m and the extension method is defined in some given instance in the implicit scope of the type of r.
classList[T]:
...
objectList:extension [T](xs: List[List[T]])
defflatten:List[T] = xs.foldLeft(Nil:List[T])(_ ++ _)
given [T:Ordering] as Ordering[List[T]]:overridedefcompare(l1: List[T], l2: List[T]):Int= ...
extension (xs: List[T])
def< (ys: List[T]):Boolean= ...
// Rule 3 - extension method available since it is in the implicit scope of List[List[Int]]List(List(1, 2), List(3, 4)).flatten // List(1, 2, 3, 4)// Rule 4 - extension method available since it is in the given Ordering[List[T]],// which is itself in the implicit scope of List[Int]List(1, 2) <List(3) // True
Summary
In this chapter we have learned:
extension methods allow adding methods to a type after the type is defined
It replaces Scala 2's implicit classes for the "Enrich my library pattern"
We can define operators with the help of extension methods
extension methods can be generic with Generic extensions with type parameters
Ease the definition of multiple extensions of a common type with Collective extensions
Extension Methods
In this exercise we will use extension methods instead of implicit classes to add methods to our types.
Make sure you're positioned at exercise "extension methods"
Follow the exercise instructions provided in the README.md file in the code folder