Last active
January 2, 2023 16:24
-
-
Save amuradyan/e249fbd0b3b61df0ce0dfa82cee6fea1 to your computer and use it in GitHub Desktop.
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
// see: http://tpolecat.github.io/2015/04/29/f-bounds.html | |
// Inheritance | |
trait Pet: | |
def name: String | |
def renamed(newName: String): Pet | |
// This workds | |
case class Fish(name: String, age: Int) | |
extends Pet: | |
def renamed(newName: String): Fish = copy(name = newName) | |
val fish = Fish("Nemo", 5) | |
// fish: Fish = Fish(Nemo,5) | |
fish.renamed("Dory") | |
// res0: Fish = Fish(Dory,5) | |
// Not good, though intentionally so | |
case class Dog(name: String, age: Int) | |
extends Pet: | |
def renamed(newName: String): Fish = new Fish(newName, age) | |
val dog = Dog("Fido", 5) | |
// dog: Dog = Dog(Fido,5) | |
dog.renamed("Rex") | |
// res1: Fish = Fish(Rex,5) | |
// Unable to abstract over it, won't compile | |
// | |
// def esquire[A <: Pet](pet: A): A = pet.renamed(pet.name + ", Esq.") | |
// F-Bounded Types | |
trait FPet[A <: FPet[A]]: | |
def name: String | |
def renamed(newName: String): A | |
case class FFish(name: String, age: Int) | |
extends FPet[FFish]: | |
def renamed(newName: String): FFish = copy(name = newName) | |
val ffish = FFish("Nemo", 5) | |
// ffish: FFish = FFish(Nemo,5) | |
ffish.renamed("Dory") | |
// res2: FFish = FFish(Dory,5) | |
// Abstraction!!! | |
def fesquire[A <: FPet[A]](pet: A): A = pet.renamed(pet.name + ", Esq.") | |
fesquire(ffish) | |
// res3: FFish = FFish(Nemo, Esq.,5) | |
// Still not good | |
case class FKitten(name: String, age: Int) | |
extends FPet[FFish]: | |
def renamed(newName: String): FFish = new FFish(newName, age) | |
val fkitten = FKitten("Fido", 5) | |
// fkitten: FKitten = FKitten(Fido,5) | |
fkitten.renamed("Rex") | |
// res4: FFish = FFish(Rex,5) | |
// Self Types | |
trait SPet[A <: SPet[A]]: | |
self: A => | |
def name: String | |
def renamed(newName: String): A | |
case class SFish(name: String, age: Int) | |
extends SPet[SFish]: | |
def renamed(newName: String): SFish = copy(name = newName) | |
val sfish = SFish("Nemo", 5) | |
// sfish: SFish = SFish(Nemo,5) | |
sfish.renamed("Dory") | |
// res5: SFish = SFish(Dory,5) | |
// This won't compile anymore | |
// | |
// case class SKitten(name: String, age: Int) | |
// extends SPet[SFish]: | |
// def renamed(newName: String): SFish = new SFish(newName, age) | |
// Subtyping, unfortunately, allows breaking the strictness anyways | |
// | |
// class Mammal(val name: String) | |
// extends SPet[Mammal]: | |
// def renamed(newName: String): Mammal = new Mammal(newName) | |
// | |
// class Monkey extends Mammal("Monkey") | |
// | |
// val monkey = new Monkey | |
// val mammal = Mammal("Mammal") | |
// | |
// monkey.isInstanceOf[Mammal] | |
// Type Classes | |
trait TCPet: | |
def name: String | |
trait TCRename[A]: | |
def renamed(a: A, newName: String): A | |
case class TCFish(name: String, age: Int) extends TCPet | |
object TCRename: | |
implicit val fishRename: TCRename[TCFish] = new TCRename[TCFish]: | |
def renamed(fish: TCFish, newName: String): TCFish = fish.copy(name = newName) | |
implicit class TCRenamedOps[A](a: A)(implicit ev: TCRename[A]): | |
def renamed(newName: String): A = ev.renamed(a, newName) | |
val tcfish = TCFish("Nemo", 5) | |
// tcfish: TCFish = TCFish(Nemo,5) | |
tcfish.renamed("Dory") | |
// res6: TCFish = TCFish(Dory,5) | |
def esquire[A <: TCPet : TCRename](pet: A): A = pet.renamed(pet.name + ", Esq.") | |
esquire(tcfish) | |
// res7: TCFish = TCFish(Nemo, Esq.,5) | |
// Type Classes Only | |
trait TCOPet[A]: | |
def name(a: A): String | |
def rename(a: A, newName: String): A | |
implicit class TCOPetOps[A](a: A)(implicit ev: TCOPet[A]): | |
def name: String = ev.name(a) | |
def renamed(newName: String): A = ev.rename(a, newName) | |
case class TCOFish(name: String, age: Int) | |
object TCOFish: | |
implicit val fishPet: TCOPet[TCOFish] = new TCOPet[TCOFish]: | |
def name(fish: TCOFish): String = fish.name | |
def rename(fish: TCOFish, newName: String): TCOFish = fish.copy(name = newName) | |
val tcofish = TCOFish("Nemo", 5) | |
// tcofish: TCOFish = TCOFish(Nemo,5) | |
tcofish.name | |
// res8: String = Nemo | |
tcofish.renamed("Dory") | |
// res9: TCOFish = TCOFish(Dory,5) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment