ºÝºÝߣ

ºÝºÝߣShare a Scribd company logo
Returning the
"Current" Type in Scala
http://tpolecat.github.io/
2015/04/29/f-bounds.html
1.
2.
Needs
.
..?
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)
}
.
scala> val a = Fish("Jimmy", 2)
a: Fish = Fish(Jimmy,2)
// renamed Fish .
scala> val b = a.renamed("Bob")
b: Fish = Fish(Bob,2)
...
...
Trait Pet .
case class Kitty(name: String, color: Color) extends Pet {
def renamed(newName: String): Fish = new Fish(newName, 42) // oops
}
Oops, ...
The problem
? Trait renamed Pet Pet Fish
.
? . Compiler
.
? .
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 .
2. F-Bounded Types
.
F-bounded typs type parameter
.
superclass .
Pet[A <: Pet[A]]
.
.
.
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
.
.
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)
...
.
case class Kitty(name: String, color: Color) extends Pet[Fish] { // oops
def renamed(newName: String): Fish = new Fish(newName, 42)
}
;;
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
.
....
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
Typeclass ?
Pet rename typeclass .
trait Pet {
def name: String
}
// rename Pet
trait Rename[A] {
def rename(a: A, newName: String): A
}
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)
}
}
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
.
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)

More Related Content

F bound-types

  • 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, ...
  • 7. The problem ? Trait renamed Pet Pet Fish . ? . Compiler . ? .
  • 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 .
  • 9. 2. F-Bounded Types . F-bounded typs type parameter . superclass . Pet[A <: Pet[A]] . . .
  • 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)