Created
October 10, 2022 18:25
-
-
Save arturaz/270d821a8171d3940a0c610e472f8c88 to your computer and use it in GitHub Desktop.
Code for "Scala =:= and implicits demystified " video (https://www.youtube.com/watch?v=4jrHAmKx7YE)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.implicit_constraints | |
object Bounds1 { | |
case class Container[A](a: A) | |
implicit class IntContainerExtensions(container: Container[Int]) { | |
def addWithExtension(other: Int): Int = container.a + other | |
} | |
val intContainer: Container[Int] = Container(5) | |
val someInt1: Int = new IntContainerExtensions(intContainer).addWithExtension(10) | |
val someInt2: Int = intContainer.addWithExtension(10) | |
val stringContainer: Container[String] = Container("5") | |
// new IntContainerExtensions(stringContainer).addWithExtension(10) | |
// stringContainer.addWithExtension(10) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.implicit_constraints | |
object Bounds2 { | |
implicit class IntContainerExtensions(container: Container[Int]) { | |
def addWithExtension(other: Int): Int = container.a + other | |
} | |
case class Container[A](a: A) { | |
/** | |
* @param evidence function that turns [[A]] to [[Int]]. | |
*/ | |
def addWithConstraint(other: Int)(implicit evidence: A =:= Int /** same as `=:=[A, Int]` */): Int = { | |
a + other | |
} | |
} | |
val someInt: Int = Container(5).addWithConstraint(10) | |
// Container("5").addWithConstraint(10) | |
// Container("5").addWithConstraint(10)(<:<.refl[Int]) | |
Container(5).addWithConstraint(10) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.implicit_constraints | |
object Bounds3 { | |
class Animal { | |
def speak: String = "Grmh" | |
} | |
class Dog extends Animal { | |
override def speak = "Woof!" | |
} | |
case class Container[A](a: A) { | |
/** | |
* @param evidence function that turns [[A]] to [[Animal]]. | |
*/ | |
def extractSpeechWithConstraint(implicit evidence: A <:< Animal): String = { | |
evidence(a).speak | |
} | |
} | |
implicit class AnimalContainerExtensions(container: Container[Animal]) { | |
def extractSpeechWithExtension: String = container.a.speak | |
} | |
implicit class AnimalContainerExtensions2[A <: Animal](val container: Container[A]) extends AnyVal { | |
def extractSpeechWithExtension: String = container.a.speak | |
} | |
val someString1: String = Container(new Animal).extractSpeechWithExtension | |
val someString4: String = Container(new Dog).extractSpeechWithExtension | |
val someString2: String = Container(new Dog).extractSpeechWithConstraint | |
val someString3: String = Container(new Animal).extractSpeechWithConstraint | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.implicit_constraints | |
import app.Functor | |
object Bounds4 { | |
case class Container[A](a: A) { | |
/** | |
* Extracts the 'context' from the inner layer | |
* (like `Container[ Option[Int] ]`) into the outer layer | |
* (like `Option[ Container[Int] ]`). | |
* | |
* @param evidence function that turns [[A]] into `F[B]`. | |
* @param functor provides the way to turn `F[A]` into `F[SomethingElse]`. | |
* @tparam F type of 'context', like `Option[A]`, `List[A]`, `Future[A]`, etc. | |
* @tparam B type of value in the 'context', like [[Int]], [[String]], etc. | |
* @return container of the value [[B]] wrapped in the 'context' [[F]]. | |
*/ | |
def extract[F[_], B](implicit evidence: A =:= F[B], functor: Functor[F]): F[Container[B]] = { | |
val fb: F[B] = evidence(a) | |
val fContainerB: F[Container[B]] = functor.map(fb) { (b: B) => | |
Container(b) | |
} | |
fContainerB | |
} | |
} | |
object Container { | |
implicit val functor: Functor[Container] = new Functor[Container] { | |
override def map[A, B](container: Container[A])(f: A => B) = Container(f(container.a)) | |
} | |
} | |
val optionOfContainer: Option[Container[Int]] = Container(Option(5)).extract(<:<.refl, Functor.optionFunctor) | |
val optionOfContainer1: Option[Container[Int]] = Container(Option(5)).extract | |
val listOfContainer: List[Container[Int]] = Container(List(1, 2, 3)).extract(<:<.refl, Functor.listFunctor) | |
// Container("5").extract | |
// Container("5").extract(<:<.refl[String], ???) | |
// Container("5").extract(<:<.refl[Option[String]], ???) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app | |
/** | |
* @tparam F any generic type that has 1 type parameter itself, like `List[A]`, `Option[A]`, `Vector[A]`, `Future[A]` | |
* and so on. | |
*/ | |
trait Functor[F[_]] { | |
/** Given an instance of `F[A]` and a function that can turn [[A]] into [[B]] returns `F[B]`. */ | |
def map[A, B](fa: F[A])(f: A => B): F[B] | |
} | |
object Functor { | |
implicit val optionFunctor: Functor[Option] = new Functor[Option] { | |
override def map[A, B](fa: Option[A])(f: A => B) = fa match { | |
case None => None | |
case Some(value) => Some(f(value)) | |
} | |
} | |
implicit val listFunctor: Functor[List] = new Functor[List] { | |
override def map[A, B](fa: List[A])(f: A => B) = fa match { | |
case Nil => Nil | |
case head :: next => f(head) :: map(next)(f) | |
} | |
} | |
implicit val vectorFunctor: Functor[Vector] = new Functor[Vector] { | |
override def map[A, B](fa: Vector[A])(f: A => B) = fa.map(f) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment