Skip to content

Instantly share code, notes, and snippets.

@amuradyan
Last active January 2, 2023 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amuradyan/e249fbd0b3b61df0ce0dfa82cee6fea1 to your computer and use it in GitHub Desktop.
Save amuradyan/e249fbd0b3b61df0ce0dfa82cee6fea1 to your computer and use it in GitHub Desktop.
// 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