2

I wrote a basic Algebraic Data Type defined as follows

sealed trait Fruit {def name: String}
case class Apple(name: String) extends Fruit
case class Orange(name: String, color: String) extends Fruit

What I'd love to do is to define a common method across Apple and Orange. So I decided to offer this feature by a Type Class pattern.

sealed trait ServingFruit[T] {
    def peel(fruit: T): String
}

object FruitManager {
    def retrievePeelColor[T: ServingFruit](fruit: T): String =
        implicitly[ServingFruit[T]].peel(fruit)

    implicit object ApplePeelColor extends ServingFruit[Apple] {
        def peel(fruit: Apple): String = "GREEN"
    }

    implicit object OrangePeelColor extends ServingFruit[Orange] {
        def peel(fruit: Orange): String = fruit.color
    }
}

For a necessary (and unfortunate) constraint I have to handle fruits as bounded instances of the shared base trait Fruit

def myCodeMethod[F <: Fruit](fruit: F, out: String) = {
    import FruitManager._
    FruitManager.retrievePeelColor(fruit)
}

And it brings me to the following (somehow expected) exception.

could not find implicit value for evidence parameter of type my.path.to.fruit.ServingFruit[F] [error]FruitManager.retrievePeelColor(fruit)

Then, AFAIU here and here Type Class Pattern is type independent and perhaps the latter is not well fitting my scenario.

The point is I'm struggling on figuring out a valuable solution to integrate my ADT with a common method available also to the base trait and - at the same time - I'd like to avoid providing the methods within the ADT (I'd try to stay FP oriented) and to use such workarounds as to add an additional Fruit converter to my Type Class.

Any help'd be really appreciated, thank you.

Andrea

spi-x-i
  • 287
  • 1
  • 12

1 Answers1

2

You need to make the type class witness passed into retrievePeelColor as an implicit parameter:

scala> def retrievePeelColor[T](fruity: T)(implicit peeler: ServingFruit[T]) = peeler.peel(fruity)
retrievePeelColor: [T](fruity: T)(implicit peeler: ServingFruit[T])String

scala> retrievePeelColor(Apple("granny smith"))
res0: String = GREEN

scala> retrievePeelColor(Orange("bloody", "RED"))
res1: String = RED

And as for the design: I'm not an experienced design guy, but I wouldn't say that having some methods in a sealed trait isn't "FP style". If only fruits are peelable, and there are finitely many, it's OK to have one place for peel, IMHO (by this I'm talking about a method in Fruit matching on this, or a static member in the Fruit companion object).

phipsgabler
  • 20,535
  • 4
  • 40
  • 60
  • This still don't solve the issue: if I call the method from `myCodeMethod` it still seeks for the implicit for evidence `ServingFruit[F]`, where `F <: Fruit`. Of course neither I am a design expert, but I'd like to avoid to provide the method by declaring it in `Fruit` and defining it for each fruit, because I want to keep the logic the code semantic separated from the Fruit ADT. – spi-x-i Jun 29 '17 at 19:17
  • Rename `FruitManager` to `ServingFruit` and it will work. Or just `import FruitManager._` – pedrofurla Jun 29 '17 at 21:48