Understanding type class, with illustration of simple type class Speaking which accepts a creature.


// Base type
sealed trait Animal
case class Dog(name:String) extends Animal
case class Cat(name:String) extends Animal

// Type class
trait Speaking [A] {
  def speak(creature : A) : String
}

// Type class instance
implicit val speakingDog = new Speaking[Dog] {
  def speak(dog : Dog) = s"I am speaking dog called ${dog.name}"
}

// Instance
val lassie = Dog("Lassie")

// Direct use of implicit instance using implicitly
implicitly[Speaking[Dog]].speak(lassie)

// Interface Object
object Speaking {
  def speak[A : Speaking](creature : A) = implicitly[Speaking[A]].speak(creature)
}

// Scala with Cats call this Interface Object, FP Simplified call this explicit way
Speaking.speak(lassie)

object SpeakingSyntax {
  implicit class SpeakingCreature[A](creature : A) {
    def speak(implicit  speakingA : Speaking[A]) : String = speakingA.speak(creature)
  }
}

// Interface syntax version
import SpeakingSyntax._

lassie.speak