Skip to content

Instantly share code, notes, and snippets.

@jiamingd
Last active August 25, 2017 19:10
Show Gist options
  • Save jiamingd/29c7c8ca639b3c8f366088e0b651040a to your computer and use it in GitHub Desktop.
Save jiamingd/29c7c8ca639b3c8f366088e0b651040a to your computer and use it in GitHub Desktop.
f-bound vs type inference drill
trait Pet {
def name : String
}
trait Renamable[A] {
def rename( a: A, newName: String): A
}
case class Fish(name: String, age: Int)
object Fish {
// Type class
implicit val fishRename = new Renamable[Fish] {
override def rename(f: Fish, newName: String): Fish = {
f.copy(name = newName)
}
}
}
//implicit class must have primary constructor with just 1 args in first curreied param lsit
implicit class HelpTool[A](a:A)(implicit renamable: Renamable[A]) {
def doRename[A](newName: String)= {
renamable.rename(a, newName)
}
}
val oldFish = Fish("Jon", 2)
val oldFishNewName = oldFish.doRename("Mike")
//This is just to show why we need F-Bound type
trait Pet {
def name: String
def renamed(newName: String): Pet
// This Approach fail on compile
// def esquire[A <: Pet](a: A): A = a.renamed(a.name + ", Esq.")
}
case class Fish(name: String, age: Int) extends Pet {
// This is allowed because return types are in covariant position;
// it’s always ok to return something more specific than what is promised.
def renamed(newName: String): Fish = copy(name = newName)
}
case class Kitty(name: String, color: String) extends Pet {
def renamed(newName: String): Fish = new Fish(newName, 42) // oops, Not Cat? We need more constrain
}
// An F-bounded type is parameterized over its own subtypes, which allows us to “pass” the
// implementing type as an argument to the superclass.
// demo F-bound polymorhism
trait Juicable[E <: Juicable[E]] { self: E => //restrict the implementing class claiming to be an A to actually be an A
def makeJuice(id: String): E
}
case class Apple(name: String, age: Int) extends Juicable[Apple] {
//restrict the implementing class claiming to be an A to actually be an A
override def makeJuice(id: String): Apple = Apple("a", 8)
}
// Below will fail compile as E conflict on Apple vs Orange, because of "self: E =>"
//case class Orange(name: String, age: Int) extends Juicable[Apple]
// But looks like this still working for return type change !!! ?????
case class Orange(name: String, age: Int) extends Juicable[Orange] {
//restrict the implementing class claiming to be an A to actually be an A
override def makeJuice(id: String): Apple = Apple("a", 8)
}
trait Juicable[E] {
def makeJuice(): Unit
}
case class Apple(name: String, weight: Int)
object Apple {
// companion object
implicit val ja = new Juicable[Apple] {
override def makeJuice(): Unit = {
println(s"making juice from apple")
}
}
}
def execJuice[F]( f : F)(implicit juicable: Juicable[F]) = {
juicable.makeJuice()
}
val a = Apple("fuji", 10)
execJuice(a)
@jiamingd
Copy link
Author

https://gist.github.com/jiamingd/29c7c8ca639b3c8f366088e0b651040a#file-playfbound-L2 seems if the self: E=> declaration removed the compiler will still tell you type incompatible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment