Reputation: 20653
Goal: I would like to write
feedImplicitInstance[Cat](new CatFood())`
and have the same effect as
feedExplicitInstance(Cat.CatInstance)(new CatFood())
How can I do that ? Is it possible to do that at all ?
This is what I tried (but it did not really work):
object DepType extends App{
println("bla")
def feedExplicitInstance[AnimalInstance]
(animal:AnimalTypeClass[AnimalInstance])(food:animal.FoodThatAnimalLikes) = {
animal.feed(food)
}
// Does not compile:
def feedImplicitInstance[AnimalInstance,Food](food:Food)
(implicit animal:AnimalTypeClass[AnimalInstance],aux:Cat.Aux[Food,AnimalInstance]) = {
animal.feed(food)
// Error:(17, 17) type mismatch;
// found : food.type (with underlying type Food)
// required: animal.FoodThatAnimalLikes
// animal.feed(food)
}
feedExplicitInstance(Cat.CatInstance)(new CatFood())
}
trait Food{
def eat():Unit
}
trait AnimalTypeClass [AnimalInstance] {
type FoodThatAnimalLikes <: Food
def feed(f:FoodThatAnimalLikes)=f.eat()
}
trait Cat
class CatFood extends Food{
override def eat(): Unit = println("meow")
}
object Cat {
type Aux[Food,Animal]= AnimalTypeClass[Animal] {type FoodThatAnimalLikes = Food}
implicit object CatInstance extends AnimalTypeClass[Cat]{
override type FoodThatAnimalLikes = CatFood
}
}
Upvotes: 1
Views: 216
Reputation: 20653
Using Yuval's answer, this is how my modified code looks like:
object DepType2 extends App{
println("bla")
def feedExplicitInstance[AnimalInstance]
(animal:AnimalTypeClass[AnimalInstance])(food:animal.FoodThatAnimalLikes) = {
animal.feed(food)
}
def feedImplicitInstance[AnimalInstance,Food](food:Food)
(implicit animal:AnimalTypeClass[AnimalInstance],aux:AnimalTypeClass.Aux[Food,AnimalInstance]) = {
aux.feed(food)
}
feedExplicitInstance(AnimalTypeClass.CatInstance)(new CatFood())
feedImplicitInstance(new CatFood())
}
trait Food{
def eat():Unit
}
trait AnimalTypeClass [AnimalInstance] {
type FoodThatAnimalLikes <: Food
def feed(f:FoodThatAnimalLikes)=f.eat()
}
trait Cat
class CatFood extends Food{
override def eat(): Unit = println("meow")
}
object AnimalTypeClass {
type Aux[Food,Animal]= AnimalTypeClass[Animal] {type FoodThatAnimalLikes = Food}
implicit object CatInstance extends AnimalTypeClass[Cat]{
override type FoodThatAnimalLikes = CatFood
}
}
So I had to change animal
to aux
in feedImplicitInstance
and object Cat
into object AnimalTypeClass
and now everything works fine.
Of course the original question was a little bit trickier:
How can I write feedImplicitInstance[Cat](new CatFood())
?
Jasper's answer is the answer for the original question. I have not understood his answer yet perfectly - I need to find some time to read carefully, hopefully very soon.
Upvotes: 0
Reputation: 149628
If we define Aux
like this:
object AnimalTypeClass {
type Aux[A, F] = AnimalTypeClass[A] { type FoodThatAnimalLikes = F }
implicit object CatInstance extends AnimalTypeClass[Cat] {
override type FoodThatAnimalLikes = CatFood
}
}
We can then summon the right implicit typeclass via a method:
def feed[A, F <: Food](f: F)(implicit animalTypeClass: AnimalTypeClass.Aux[A, F]) = {
animalTypeClass.feed(f)
}
And now this compiles and runs:
feed(new CatFood())
I changed the name of the generic type parameters a bit, but they're largely the same as in your example. Just note the implicit instance definition changes.
Upvotes: 1
Reputation: 15086
First of all, it makes more sense to put Aux
in the AnimalTypeClass
companion object, and switch the order of its type parameters. Though none of this is required to make it compile.
In order to enable your preferred calling convention of feedImplicitInstance[Cat](new CatFood())
feedImplicitInstance
is allowed to have only one type parameter. But Food
must be a type parameter because forward references like animal.FoodThatAnimalLikes
in parameter lists are not allowed, as you probably noticed yourself. That's why you needed the Aux
in the first place. To reconcile those conflicting constraints, you should manually implement a sort of type parameter currying. That's what the Feeder
class in the following complete example is for:
object DepType extends App {
def feedExplicitInstance[AnimalInstance]
(animal: AnimalTypeClass[AnimalInstance])(food: animal.FoodThatAnimalLikes) = {
animal.feed(food)
}
class Feeder[AnimalInstance] {
def apply[F <: Food](food: F)(implicit animal: AnimalTypeClass.Aux[AnimalInstance, F]) =
animal.feed(food)
}
def feedImplicitInstance[AnimalInstance] = new Feeder[AnimalInstance]
feedExplicitInstance(Cat.CatInstance)(new CatFood())
feedImplicitInstance[Cat](new CatFood())
}
trait Food{
def eat(): Unit
}
class CatFood extends Food{
override def eat(): Unit = println("meow")
}
trait AnimalTypeClass[AnimalInstance] {
type FoodThatAnimalLikes <: Food
def feed(f: FoodThatAnimalLikes) = f.eat()
}
object AnimalTypeClass {
type Aux[A, F <: Food]= AnimalTypeClass[A] {type FoodThatAnimalLikes = F}
}
trait Cat
object Cat {
implicit object CatInstance extends AnimalTypeClass[Cat]{
override type FoodThatAnimalLikes = CatFood
}
}
Upvotes: 1