This document discusses different approaches to implementing a generic "rename" method in Scala that works across different case classes that extend a common trait. It covers using F-bounded polymorphism, self-type annotations, and type classes to allow classes like Fish and Kitty to each rename themselves while ensuring type safety. F-bounded polymorphism allows the rename method to return the specific subclass type, self-types avoid illegal inheritance, and type classes provide ad-hoc polymorphism through implicit parameters and evidence.
4. OOP .
trait Pet {
def name: String
def renamed(newName: String): Pet
}
case class Fish(name: String, age: Int) extends Pet {
def renamed(newName: String): Fish = copy(name = newName)
}
5. .
scala> val a = Fish("Jimmy", 2)
a: Fish = Fish(Jimmy,2)
// renamed Fish .
scala> val b = a.renamed("Bob")
b: Fish = Fish(Bob,2)
...
6. ...
Trait Pet .
case class Kitty(name: String, color: Color) extends Pet {
def renamed(newName: String): Fish = new Fish(newName, 42) // oops
}
Oops, ...
8. 1. .
general .
def esquire[A <: Pet](a: A): A = a.renamed(a.name + ", Esq.")
<console>:28: error: type mismatch;
found : Pet
required: A
def esquire[A <: Pet](a: A): A = a.renamed(a.name + ", Esq.")
^
A <: Pet
. Pet .
10. F-Bounded
trait Pet[A <: Pet[A]] {
def name: String
def renamed(newName: String): A // note this return type
}
A Pet renamed A .
Pet Trait .
Trait
.
11. .
case class Fish(name: String, age: Int) extends Pet[Fish] { // note the type argument
def renamed(newName: String) = copy(name = newName)
}
scala> val a = Fish("Jimmy", 2)
a: Fish = Fish(Jimmy,2)
scala> val b = a.renamed("Bob")
b: Fish = Fish(Bob,2)
// generic rename . Pet[A] renamed A .
scala> def esquire[A <: Pet[A]](a: A): A = a.renamed(a.name + ", Esq.")
esquire: [A <: Pet[A]](a: A)A
scala> esquire(a)
res8: Fish = Fish(Jimmy, Esq.,2)
12. ...
.
case class Kitty(name: String, color: Color) extends Pet[Fish] { // oops
def renamed(newName: String): Fish = new Fish(newName, 42)
}
;;
13. self type annotation .
trait Pet[A <: Pet[A]] { this: A => // self-type
def name: String
def renamed(newName: String): A
}
self type annotation .
```scala
case class Kitty(name: String, color: Color) extends Pet[Fish] {
def renamed(newName: String): Fish = new Fish(newName, 42)
}
<console>:19: error: illegal inheritance;
self-type Kitty does not conform to Pet[Fish]'s selftype Fish
case class Kitty(name: String, color: Color) extends Pet[Fish] {
^
Fish Kitty
.
14. ....
Pet class class .
class Mammal(val name: String) extends Pet[Mammal] {
def renamed(newName: String) = new Mammal(newName)
}
class Monkey(name: String) extends Mammal(name) // hmm, Monkey is a Pet[Mammal]
res0: Mammal = Mammal@5587afd3
!
OTL
15. Typeclass ?
Pet rename typeclass .
trait Pet {
def name: String
}
// rename Pet
trait Rename[A] {
def rename(a: A, newName: String): A
}
16. Typeclass
case class Fish(name: String, age: Int) extends Pet
object Fish {
// Fish rename Rename Trait .
implicit val FishRename = new Rename[Fish] {
def renamed(a: Fish, newName: String) = a.copy(name = newName)
}
}
17. Typeclass implicit class
implicit class Pet rename
.
implicit class RenameOps[A](a: A)(implicit ev: Rename[A]) {
def renamed(newName: String) = ev.renamed(a, newName)
}
scala> val a = Fish("Jimmy", 2)
a: Fish = Fish(Jimmy,2)
scala> val b = a.renamed("Bob")
b: Fish = Fish(Bob,2)
// F bounded
scala> def esquire[A <: Pet : Rename](a: A): A = a.renamed(a.name + ", Esq.")
esquire: [A <: Pet](a: A)(implicit evidence$1: Rename[A])A
scala> esquire(a)
res10: Fish = Fish(Jimmy, Esq.,2)
syntax
.
18. Typeclass ?
! ad-hoc polymorphism
trait Pet[A] {
def name(a: A): String
def renamed(a: A, newName: String): A
}
implicit class PetOps[A](a: A)(implicit ev: Pet[A]) {
def name = ev.name(a)
def renamed(newName: String): A = ev.renamed(a, newName)
}
!
case class Fish(name: String, age: Int)
object Fish {
implicit val FishPet = new Pet[Fish] {
def name(a: Fish) = a.name
def renamed(a: Fish, newName: String) = a.copy(name = newName)
}
}
// renamed `PetOps` .
scala> Fish("Bob", 42).renamed("Steve")
res0: Fish = Fish(Steve,42)