SlideShare une entreprise Scribd logo
1  sur  31
Télécharger pour lire hors ligne
Scala 3 by example
better Semigroup and Monoid
as a pretext for learning the basics of some new Scala 3 features
we take two very simple Semigroup and Monoid typeclasses and make them a bit better
by migrating them to Scala 3
@philip_schwarzslides by
https://www.slideshare.net/pjschwarz
Here is the code we are going to play around with in this slide
deck. It is not production code. It was put together purely as a
simple playground for experimenting with some Scala 3 features.
trait Semigroup[A] {
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
}
implicit val intSemigroup = new Semigroup[Int] {
def combine(l: Int, r: Int): Int = l + r
}
implicit val stringSemigroup = new Semigroup[String] {
def combine(l: String, r: String): String = l + r
}
object Semigroup {
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
}
object Syntax {
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) {
def |+|(other: A): A = semigroup.combine(a, other)
}
}
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
@philip_schwarz
trait Semigroup[A] {
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
}
implicit val intSemigroup = new Semigroup[Int] {
def combine(l: Int, r: Int): Int = l + r
}
implicit val stringSemigroup = new Semigroup[String] {
def combine(l: String, r: String): String = l + r
}
object Semigroup {
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
}
object Syntax {
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) {
def |+|(other: A): A = semigroup.combine(a, other)
}
}
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
The first thing we are going to do is
1. switch the compiler from Scalac to Dotty
2. introduce a main method using the new @main annotation
3. replace all curly brace pairs with the with keyword
From https://dotty.epfl.ch/docs/reference/changed-features/main-functions.html
Main Methods
Scala 3 offers a new way to define programs that can be invoked from the
command line: A @main annotation on a method turns this method into an
executable program.
From https://dotty.epfl.ch/docs/reference/other-new-features/indentation-
new.html
Optional Braces
As an experimental feature, Scala 3 enforces some rules on indentation and
allows some occurrences of braces {...} to be optional.
• First, some badly indented programs are ruled out, which means they are
flagged with warnings.
• Second, some occurrences of braces {...} are made optional. Generally, the
rule is that adding a pair of optional braces will not change the meaning of a
well-indented program.
…
New Role of With
To make braces optional for constructs like class bodies, the syntax of the
language is changed so that a class body or similar construct may optionally be
prefixed with with.
…
before the changes after the changes
trait Semigroup[A] {
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
}
implicit val intSemigroup = new Semigroup[Int] {
def combine(l: Int, r: Int): Int = l + r
}
implicit val stringSemigroup = new Semigroup[String] {
def combine(l: String, r: String): String = l + r
}
object Semigroup {
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
}
object Syntax {
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) {
def |+|(other: A): A = semigroup.combine(a, other)
}
}
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
object Syntax with
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) with
def |+|(other: A): A = semigroup.combine(a, other)
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
before the changes after the changes
trait Semigroup[A] {
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
}
implicit val intSemigroup = new Semigroup[Int] {
def combine(l: Int, r: Int): Int = l + r
}
implicit val stringSemigroup = new Semigroup[String] {
def combine(l: String, r: String): String = l + r
}
object Semigroup {
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
}
object Syntax {
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) {
def |+|(other: A): A = semigroup.combine(a, other)
}
}
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
latest
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
object Syntax with
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) with
def |+|(other: A): A = semigroup.combine(a, other)
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
The current version of the code uses an implicit SemigroupSyntax
class to implement extension methods so as to provide |+|, the Tie
Fighter operator, as an infix alias for combine.
What we are going to do next is use Scala 3’s extension method
feature, which relaces implicit classes with a clearer and simpler
mechanism.
“You may occasionally see
extension methods referred
to as “type enrichment” or
“pimping”. These are older
terms that we don’t use
anymore.”
before the changes after the changes
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
object Syntax with
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) with
def |+|(other: A): A = semigroup.combine(a, other)
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
before the changes after the changes
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
latest
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
@philip_schwarz
The way the combineAllOption function of Semigroup is
accessed in the current version of the code is via a Semigroup
implicit instance that is summoned by calling Semigroup’s apply
function, e.g.
Semigroup[Int].combineAllOption( List(2,3,4) )
What we are going to do next is make combineAllOption an
extension method, so that it can be invoked directly on anything
for which there is an implicit Semigroup instance.
before the changes after the changes
before the changes after the changes
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
latest
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
The current version of
the code uses implicits
to implement the
Semigroup typeclass.
So what we are going
to do next is switch
from implicits to
givens.
before the changes after the changes
before the changes after the changes
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
implicit val intSemigroup = new Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
implicit val stringSemigroup = new Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](given semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](given semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
latest
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](given semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
Now let’s simplify the apply function of
Semigroup by using the new predefined
summon function.
before the changes after the changes
before the changes after the changes
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A](given semigroup: Semigroup[A]) = semigroup
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A]((given Semigroup[A]) = summon[Semigroup[A]]
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A]((given Semigroup[A]) = summon[Semigroup[A]]
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
latest
@main def main =
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
object Semigroup with
def apply[A]((given Semigroup[A]) = summon[Semigroup[A]]
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
trait Semigroup[A] {
def combine(l: A, r: A): A
def combineAllOption(as: Seq[A]): Option[A] =
as.reduceOption(combine(_,_))
def combineOption(as: A*): Option[A] =
combineAllOption(as)
}
implicit val intSemigroup = new Semigroup[Int] {
def combine(l: Int, r: Int): Int = l + r
}
implicit val stringSemigroup = new Semigroup[String] {
def combine(l: String, r: String): String = l + r
}
object Semigroup {
def apply[A](implicit semigroup: Semigroup[A]) = semigroup
}
object Syntax {
implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) {
def |+|(other: A): A = semigroup.combine(a, other)
}
}
import Syntax._
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( Semigroup[String].combineAllOption( List() ) == None)
assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9))
assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
@philip_schwarz
Here is the original code and next to it the
version with all the changes we have made.
In the next slide we’ll see Martin Odersky introduce
givens. In the process, we’ll briefly see an aspect of givens
that we have not used here, i.e. conditional givens.
Given is the new implicit.
So, given instances. Here is a first introductory sample, it’s the one that we mostly start with, when we do typeclasses, we want to explain what orderings are for Ints and Lists of T,
so here is a trait, Ord, it has three methods, compare, less than and greater than. So that’s essentially another new thing in Scala 3, we can write extension methods like that, we just
say this method is applied infix, and that’s the left argument and that is the right argument. OK, and now we say well we want to have two instances for ordering. Integers are ordered
and Lists are ordered. In both of these cases we have to explain what compare is because compare is an abstract method here and the other two are already implemented.
So for intOrd we have this usual compare that you see here, and for listOrd we have a slightly longer compare that I have left out, but the important part for the listOrd is that it
works for any T, for which there is an Ord[T], of course, that’s the conditional implicit and then if you have an Ord[T] you can construct an Ord[List[T]] and that is what its
compare method is.
So you see that whether it is conditional or not, previously it was like an implicit object or an implicit def or an implicit class and all these things had to be constructed in a very subtle
way, now it is just given, you say you have a given instance for a given type and then we explain essentially what is missing and that’s all.
Lambda World 2019
Implicits Revisited – Martin Odersky
The other new thing is that the names of these given instances can also be left out. So
you can also write it like that. You can just say, there is a given ordering of Int where that
is the compare method and here you say for any T that itself has an ordering there is a
given ordering of List[T] where the compare method is this, because the names of
these things after all they don’t matter because you will usually not refer to these things.
The point of givens or implicits is that the compiler will produce them for you, so why
should you bother giving them a name? There could be reasons for giving them a name of
course, the main reason is binary stability, you don’t want to rely on the compiler inferring
names and maybe inferring different names in different versions, that way you can
essentially nail down a name as a programmer, so for long-living APIs that actually makes
a lot of sense, but for doing things quickly you should be able to leave these things out.
Lambda World 2019
Implicits Revisited
Martin Odersky
By the way, on the right hand side,
the conditional given for
Ord[Int] has been replaced with
a context bound for Ord[Int].
Lambda World 2019 - Implicits Revisited - Martin Odersky
So you see it is actually a very quiet and nice syntax for typeclasses if you compare to what you have to do now, it is much much nicer, in particular in
the way it treats infix methods. So far, to do this in any way that is reasonable you needed essentially a macro package called Simulacrum which would
essentially add a bunch of imports to your program to make it work in a way but now it is available much more directly and much more robustly.
see also https://dotty.epfl.ch/docs/reference/contextual/typeclasses.html
This summon method that I have
shown here, that is essentially
the new implicitly.
Martin Odersky
@philip_schwarz
Let’s have a go at using what we have
just seen to add the Monoid
typeclass to our code.
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
Our existing Semigroup code,
for reference.
trait Monoid[A] extends Semigroup[A] with
def unit: A
object Monoid with
def apply[A](given Monoid[A]) = summon[Monoid[A]]
given Monoid[Int] with
def combine(l: Int, r: Int): Int = l + r
def unit: Int = 0
given Monoid[String] with
def combine(l: String, r: String): String = l + r
def unit: String = ""
assert( (2 |+| 3 |+| Monoid[Int].unit) == 5 )
assert( ("2" |+| "3" |+| Monoid[String].unit) == "23" )
def combineAll[A: Monoid](as: Seq[A]): A =
as.foldLeft(summon[Monoid[A]].unit)(_ |+| _)
assert( combineAll( List(2,3,4) ) == 9 )
assert( combineAll( List("2","3","4") ) == "234" )
def combineAll[A: Monoid](as: Seq[A]): A =
as.foldLeft(Monoid[A].unit)(_ |+| _)
def combineAll[A](as: Seq[A])(given monoid: Monoid[A]): A =
as.foldLeft(monoid.unit)(_ |+| _)
Let’s define a Monoid, a convenient way of
summoning a Monoid, and two Monoid instances.
As for the sum method shown earlier by Martin Odersky, we
could do something similar and call it combineAll, to be
consistent with what we did earlier for Semigroup.
And by the way, here is how the combineAll function would
look like if we hadn’t used a context bound.
But it is less verbose using the Monoid apply function we
have just introduced.
trait Semigroup[A] with
def combine(l: A, r: A): A
def (l: A) |+| (r: A): A = combine(l, r)
def (as: Seq[A]) combineAllOption: Option[A] =
as.reduceOption(_ |+| _)
def combineOption(as: A*): Option[A] =
combineAllOption(as)
object Semigroup with
def apply[A]((given Semigroup[A]) = summon[Semigroup[A]]
given Semigroup[Int] with
def combine(l: Int, r: Int): Int = l + r
given Semigroup[String] with
def combine(l: String, r: String): String = l + r
assert( (2 |+| 3) == 5 )
assert( ("2" |+| "3") == "23")
assert( Semigroup[Int].combineOption() == None)
assert( Semigroup[Int].combineOption(2,3,4) == Some(9))
assert( Semigroup[String].combineOption("2","3","4") == Some("234"))
assert( List().combineAllOption == None)
assert( List(2,3,4).combineAllOption == Some(9))
assert( List("2","3","4").combineAllOption == Some("234"))
trait Monoid[A] extends Semigroup[A] with
def unit: A
def (as: Seq[A]) combineAll: A =
as.foldLeft(unit)(_ |+| _)
def combine(as: A*): A =
as.combineAll
object Monoid with
def apply[A](given Monoid[A]) = summon[Monoid[A]]
given Monoid[Int] with
def combine(l: Int, r: Int): Int = l + r
def unit: Int = 0
given Monoid[String] with
def combine(l: String, r: String): String = l + r
def unit: String = ""
assert( (2 |+| 3 |+| Monoid[Int].unit) == 5 )
assert( ("2" |+| "3" |+| Monoid[String].unit) == "23" )
assert( Monoid[Int].combine() == Monoid[Int].unit)
assert( Monoid[Int].combine(2,3,4) == 9)
assert( Monoid[String].combine("2","3","4") == "234")
assert( List().combineAll == Monoid[Int].unit)
assert( List(2,3,4).combineAll == 9)
assert( List("2","3","4").combineAll == "234")
FWIW, in this final slide, just to be even more consistent with what we did earlier for Semigroup, let’s move
the combineAll function to the Monoid typeclass, so it is analogous to the combineAllOption function in the
Semigroup typeclass and let’s also add to Monoid a combine function that is analogous to the
combineOption function in the Semigroup typeclass.

Contenu connexe

Plus de Philip Schwarz

A sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in ScalaA sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in ScalaPhilip Schwarz
 
A sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaPhilip Schwarz
 
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsPhilip Schwarz
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Philip Schwarz
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...Philip Schwarz
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an examplePhilip Schwarz
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an examplePhilip Schwarz
 
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...Philip Schwarz
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...Philip Schwarz
 
Jordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axiomsJordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axiomsPhilip Schwarz
 
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Philip Schwarz
 
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Philip Schwarz
 
The Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor correctionsPhilip Schwarz
 
The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1Philip Schwarz
 
The Uniform Access Principle
The Uniform Access PrincipleThe Uniform Access Principle
The Uniform Access PrinciplePhilip Schwarz
 
Computer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1bPhilip Schwarz
 
The Expression Problem - Part 2
The Expression Problem - Part 2The Expression Problem - Part 2
The Expression Problem - Part 2Philip Schwarz
 
Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1Philip Schwarz
 
The Expression Problem - Part 1
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1Philip Schwarz
 

Plus de Philip Schwarz (20)

A sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in ScalaA sighting of traverseFilter and foldMap in Practical FP in Scala
A sighting of traverseFilter and foldMap in Practical FP in Scala
 
A sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in Scala
 
N-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
 
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
 
The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
Nat, List and Option Monoids -from scratch -Combining and Folding -an exampleNat, List and Option Monoids -from scratch -Combining and Folding -an example
Nat, List and Option Monoids - from scratch - Combining and Folding - an example
 
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
Jordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axiomsJordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axioms
 
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
 
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
 
The Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
 
The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
 
The Uniform Access Principle
The Uniform Access PrincipleThe Uniform Access Principle
The Uniform Access Principle
 
Computer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
 
The Expression Problem - Part 2
The Expression Problem - Part 2The Expression Problem - Part 2
The Expression Problem - Part 2
 
Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1
 
The Expression Problem - Part 1
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
 

Dernier

Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about usDynamic Netsoft
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...aditisharan08
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio, Inc.
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...harshavardhanraghave
 
Introduction to Decentralized Applications (dApps)
Introduction to Decentralized Applications (dApps)Introduction to Decentralized Applications (dApps)
Introduction to Decentralized Applications (dApps)Intelisync
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 

Dernier (20)

Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
DNT_Corporate presentation know about us
DNT_Corporate presentation know about usDNT_Corporate presentation know about us
DNT_Corporate presentation know about us
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...Unit 1.1 Excite Part 1, class 9, cbse...
Unit 1.1 Excite Part 1, class 9, cbse...
 
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed DataAlluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
Alluxio Monthly Webinar | Cloud-Native Model Training on Distributed Data
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Introduction to Decentralized Applications (dApps)
Introduction to Decentralized Applications (dApps)Introduction to Decentralized Applications (dApps)
Introduction to Decentralized Applications (dApps)
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 

Scala 3 by Example - better Semigroup and Monoid

  • 1. Scala 3 by example better Semigroup and Monoid as a pretext for learning the basics of some new Scala 3 features we take two very simple Semigroup and Monoid typeclasses and make them a bit better by migrating them to Scala 3 @philip_schwarzslides by https://www.slideshare.net/pjschwarz
  • 2. Here is the code we are going to play around with in this slide deck. It is not production code. It was put together purely as a simple playground for experimenting with some Scala 3 features. trait Semigroup[A] { def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) } implicit val intSemigroup = new Semigroup[Int] { def combine(l: Int, r: Int): Int = l + r } implicit val stringSemigroup = new Semigroup[String] { def combine(l: String, r: String): String = l + r } object Semigroup { def apply[A](implicit semigroup: Semigroup[A]) = semigroup } object Syntax { implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) { def |+|(other: A): A = semigroup.combine(a, other) } } import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) @philip_schwarz
  • 3. trait Semigroup[A] { def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) } implicit val intSemigroup = new Semigroup[Int] { def combine(l: Int, r: Int): Int = l + r } implicit val stringSemigroup = new Semigroup[String] { def combine(l: String, r: String): String = l + r } object Semigroup { def apply[A](implicit semigroup: Semigroup[A]) = semigroup } object Syntax { implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) { def |+|(other: A): A = semigroup.combine(a, other) } } import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) The first thing we are going to do is 1. switch the compiler from Scalac to Dotty 2. introduce a main method using the new @main annotation 3. replace all curly brace pairs with the with keyword From https://dotty.epfl.ch/docs/reference/changed-features/main-functions.html Main Methods Scala 3 offers a new way to define programs that can be invoked from the command line: A @main annotation on a method turns this method into an executable program. From https://dotty.epfl.ch/docs/reference/other-new-features/indentation- new.html Optional Braces As an experimental feature, Scala 3 enforces some rules on indentation and allows some occurrences of braces {...} to be optional. • First, some badly indented programs are ruled out, which means they are flagged with warnings. • Second, some occurrences of braces {...} are made optional. Generally, the rule is that adding a pair of optional braces will not change the meaning of a well-indented program. … New Role of With To make braces optional for constructs like class bodies, the syntax of the language is changed so that a class body or similar construct may optionally be prefixed with with. …
  • 4. before the changes after the changes
  • 5. trait Semigroup[A] { def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) } implicit val intSemigroup = new Semigroup[Int] { def combine(l: Int, r: Int): Int = l + r } implicit val stringSemigroup = new Semigroup[String] { def combine(l: String, r: String): String = l + r } object Semigroup { def apply[A](implicit semigroup: Semigroup[A]) = semigroup } object Syntax { implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) { def |+|(other: A): A = semigroup.combine(a, other) } } import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup object Syntax with implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) with def |+|(other: A): A = semigroup.combine(a, other) import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) before the changes after the changes
  • 6. trait Semigroup[A] { def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) } implicit val intSemigroup = new Semigroup[Int] { def combine(l: Int, r: Int): Int = l + r } implicit val stringSemigroup = new Semigroup[String] { def combine(l: String, r: String): String = l + r } object Semigroup { def apply[A](implicit semigroup: Semigroup[A]) = semigroup } object Syntax { implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) { def |+|(other: A): A = semigroup.combine(a, other) } } import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) latest
  • 7. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup object Syntax with implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) with def |+|(other: A): A = semigroup.combine(a, other) import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) The current version of the code uses an implicit SemigroupSyntax class to implement extension methods so as to provide |+|, the Tie Fighter operator, as an infix alias for combine. What we are going to do next is use Scala 3’s extension method feature, which relaces implicit classes with a clearer and simpler mechanism. “You may occasionally see extension methods referred to as “type enrichment” or “pimping”. These are older terms that we don’t use anymore.”
  • 8. before the changes after the changes
  • 9. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup object Syntax with implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) with def |+|(other: A): A = semigroup.combine(a, other) import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) before the changes after the changes @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234"))
  • 10. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) latest
  • 11. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) @philip_schwarz The way the combineAllOption function of Semigroup is accessed in the current version of the code is via a Semigroup implicit instance that is summoned by calling Semigroup’s apply function, e.g. Semigroup[Int].combineAllOption( List(2,3,4) ) What we are going to do next is make combineAllOption an extension method, so that it can be invoked directly on anything for which there is an implicit Semigroup instance.
  • 12. before the changes after the changes
  • 13. before the changes after the changes @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234"))
  • 14. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) latest
  • 15. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup The current version of the code uses implicits to implement the Semigroup typeclass. So what we are going to do next is switch from implicits to givens.
  • 16. before the changes after the changes
  • 17. before the changes after the changes @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) implicit val intSemigroup = new Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r implicit val stringSemigroup = new Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](implicit semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](given semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234"))
  • 18. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](given semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) latest
  • 19. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](given semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) Now let’s simplify the apply function of Semigroup by using the new predefined summon function.
  • 20. before the changes after the changes
  • 21. before the changes after the changes @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A](given semigroup: Semigroup[A]) = semigroup assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A]((given Semigroup[A]) = summon[Semigroup[A]] assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234"))
  • 22. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A]((given Semigroup[A]) = summon[Semigroup[A]] assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) latest
  • 23. @main def main = trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r object Semigroup with def apply[A]((given Semigroup[A]) = summon[Semigroup[A]] assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) trait Semigroup[A] { def combine(l: A, r: A): A def combineAllOption(as: Seq[A]): Option[A] = as.reduceOption(combine(_,_)) def combineOption(as: A*): Option[A] = combineAllOption(as) } implicit val intSemigroup = new Semigroup[Int] { def combine(l: Int, r: Int): Int = l + r } implicit val stringSemigroup = new Semigroup[String] { def combine(l: String, r: String): String = l + r } object Semigroup { def apply[A](implicit semigroup: Semigroup[A]) = semigroup } object Syntax { implicit class SemigroupSyntax[A](a: A)(implicit semigroup: Semigroup[A]) { def |+|(other: A): A = semigroup.combine(a, other) } } import Syntax._ assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( Semigroup[String].combineAllOption( List() ) == None) assert( Semigroup[Int].combineAllOption( List(2,3,4) ) == Some(9)) assert( Semigroup[String].combineAllOption( List("2","3","4") ) == Some("234")) @philip_schwarz Here is the original code and next to it the version with all the changes we have made.
  • 24. In the next slide we’ll see Martin Odersky introduce givens. In the process, we’ll briefly see an aspect of givens that we have not used here, i.e. conditional givens.
  • 25. Given is the new implicit. So, given instances. Here is a first introductory sample, it’s the one that we mostly start with, when we do typeclasses, we want to explain what orderings are for Ints and Lists of T, so here is a trait, Ord, it has three methods, compare, less than and greater than. So that’s essentially another new thing in Scala 3, we can write extension methods like that, we just say this method is applied infix, and that’s the left argument and that is the right argument. OK, and now we say well we want to have two instances for ordering. Integers are ordered and Lists are ordered. In both of these cases we have to explain what compare is because compare is an abstract method here and the other two are already implemented. So for intOrd we have this usual compare that you see here, and for listOrd we have a slightly longer compare that I have left out, but the important part for the listOrd is that it works for any T, for which there is an Ord[T], of course, that’s the conditional implicit and then if you have an Ord[T] you can construct an Ord[List[T]] and that is what its compare method is. So you see that whether it is conditional or not, previously it was like an implicit object or an implicit def or an implicit class and all these things had to be constructed in a very subtle way, now it is just given, you say you have a given instance for a given type and then we explain essentially what is missing and that’s all. Lambda World 2019 Implicits Revisited – Martin Odersky
  • 26. The other new thing is that the names of these given instances can also be left out. So you can also write it like that. You can just say, there is a given ordering of Int where that is the compare method and here you say for any T that itself has an ordering there is a given ordering of List[T] where the compare method is this, because the names of these things after all they don’t matter because you will usually not refer to these things. The point of givens or implicits is that the compiler will produce them for you, so why should you bother giving them a name? There could be reasons for giving them a name of course, the main reason is binary stability, you don’t want to rely on the compiler inferring names and maybe inferring different names in different versions, that way you can essentially nail down a name as a programmer, so for long-living APIs that actually makes a lot of sense, but for doing things quickly you should be able to leave these things out. Lambda World 2019 Implicits Revisited Martin Odersky By the way, on the right hand side, the conditional given for Ord[Int] has been replaced with a context bound for Ord[Int].
  • 27. Lambda World 2019 - Implicits Revisited - Martin Odersky So you see it is actually a very quiet and nice syntax for typeclasses if you compare to what you have to do now, it is much much nicer, in particular in the way it treats infix methods. So far, to do this in any way that is reasonable you needed essentially a macro package called Simulacrum which would essentially add a bunch of imports to your program to make it work in a way but now it is available much more directly and much more robustly. see also https://dotty.epfl.ch/docs/reference/contextual/typeclasses.html
  • 28. This summon method that I have shown here, that is essentially the new implicitly. Martin Odersky
  • 29. @philip_schwarz Let’s have a go at using what we have just seen to add the Monoid typeclass to our code.
  • 30. trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) Our existing Semigroup code, for reference. trait Monoid[A] extends Semigroup[A] with def unit: A object Monoid with def apply[A](given Monoid[A]) = summon[Monoid[A]] given Monoid[Int] with def combine(l: Int, r: Int): Int = l + r def unit: Int = 0 given Monoid[String] with def combine(l: String, r: String): String = l + r def unit: String = "" assert( (2 |+| 3 |+| Monoid[Int].unit) == 5 ) assert( ("2" |+| "3" |+| Monoid[String].unit) == "23" ) def combineAll[A: Monoid](as: Seq[A]): A = as.foldLeft(summon[Monoid[A]].unit)(_ |+| _) assert( combineAll( List(2,3,4) ) == 9 ) assert( combineAll( List("2","3","4") ) == "234" ) def combineAll[A: Monoid](as: Seq[A]): A = as.foldLeft(Monoid[A].unit)(_ |+| _) def combineAll[A](as: Seq[A])(given monoid: Monoid[A]): A = as.foldLeft(monoid.unit)(_ |+| _) Let’s define a Monoid, a convenient way of summoning a Monoid, and two Monoid instances. As for the sum method shown earlier by Martin Odersky, we could do something similar and call it combineAll, to be consistent with what we did earlier for Semigroup. And by the way, here is how the combineAll function would look like if we hadn’t used a context bound. But it is less verbose using the Monoid apply function we have just introduced.
  • 31. trait Semigroup[A] with def combine(l: A, r: A): A def (l: A) |+| (r: A): A = combine(l, r) def (as: Seq[A]) combineAllOption: Option[A] = as.reduceOption(_ |+| _) def combineOption(as: A*): Option[A] = combineAllOption(as) object Semigroup with def apply[A]((given Semigroup[A]) = summon[Semigroup[A]] given Semigroup[Int] with def combine(l: Int, r: Int): Int = l + r given Semigroup[String] with def combine(l: String, r: String): String = l + r assert( (2 |+| 3) == 5 ) assert( ("2" |+| "3") == "23") assert( Semigroup[Int].combineOption() == None) assert( Semigroup[Int].combineOption(2,3,4) == Some(9)) assert( Semigroup[String].combineOption("2","3","4") == Some("234")) assert( List().combineAllOption == None) assert( List(2,3,4).combineAllOption == Some(9)) assert( List("2","3","4").combineAllOption == Some("234")) trait Monoid[A] extends Semigroup[A] with def unit: A def (as: Seq[A]) combineAll: A = as.foldLeft(unit)(_ |+| _) def combine(as: A*): A = as.combineAll object Monoid with def apply[A](given Monoid[A]) = summon[Monoid[A]] given Monoid[Int] with def combine(l: Int, r: Int): Int = l + r def unit: Int = 0 given Monoid[String] with def combine(l: String, r: String): String = l + r def unit: String = "" assert( (2 |+| 3 |+| Monoid[Int].unit) == 5 ) assert( ("2" |+| "3" |+| Monoid[String].unit) == "23" ) assert( Monoid[Int].combine() == Monoid[Int].unit) assert( Monoid[Int].combine(2,3,4) == 9) assert( Monoid[String].combine("2","3","4") == "234") assert( List().combineAll == Monoid[Int].unit) assert( List(2,3,4).combineAll == 9) assert( List("2","3","4").combineAll == "234") FWIW, in this final slide, just to be even more consistent with what we did earlier for Semigroup, let’s move the combineAll function to the Monoid typeclass, so it is analogous to the combineAllOption function in the Semigroup typeclass and let’s also add to Monoid a combine function that is analogous to the combineOption function in the Semigroup typeclass.