I'm reading through and working my way through using type classes and I came across this way of defining type classes from the Shapeless guide:
So here goes the example:
object CsvEncoder {
// "Summoner" method
def apply[A](implicit enc: CsvEncoder[A]): CsvEncoder[A] =
enc
// "Constructor" method
def instance[A](func: A => List[String]): CsvEncoder[A] =
new CsvEncoder[A] {
def encode(value: A): List[String] =
func(value)
}
// Globally visible type class instances
}
What I do not understand is the need for the apply method? What is it doing in this context above?
Later on, the guide describes how I could create a type class instance:
implicit val booleanEncoder: CsvEncoder[Boolean] =
new CsvEncoder[Boolean] {
def encode(b: Boolean): List[String] =
if(b) List("yes") else List("no")
}
is actually shortened to:
implicit val booleanEncoder: CsvEncoder[Boolean] =
instance(b => if(b) List("yes") else List("no"))
So my question now is, how does this work? What I do not get is the need for the apply method?
EDIT: I came across a blog post that describes the steps in creating type classes as below:
- Define typeclass contract trait Foo.
- Define a companion object Foo with a helper method apply that acts like implicitly, and a way of defining Foo instances typically from a function.
- Define FooOps class that defines unary or binary operators.
- Define FooSyntax trait that implicitly provides FooOps from a Foo instance.
So what is the deal with point number 2, 3 and 4?