2

Here is a strange situation:

If I comment out the call to feed_usingExplicitTypeClassInstance below, then I get a compiler error.

Very puzzling. Any explanation ?

I mean, I comment out a function call (which returns no value) and then the code does not compile anymore ?

Should this be even possible at all in theory ? In any programming language ?

I mean I comment out something like println("hello") and then the code does not compile anymore ?

Of course it would be understandable if I would comment out a declaration or something, but a call to a function that does not return anything ?

object AnimalFeeder extends App {

  def feed_usingExplicitTypeClassInstance[AnimalInstance]
    (animalTypeClass: AnimalTypeClass[AnimalInstance])
    (food: animalTypeClass.FoodThatAnimalLikes) =
      {
          animalTypeClass.feed(food)
      }

  def feed_usingImplicitTypeClassInstance[AnimalInstance, Food]
    (food: Food)
    (implicit animalTypeClass: AnimalTypeClass.Aux[Food,AnimalInstance]) =
      {
        animalTypeClass.feed(food)
      }


  // If I comment out this line, THEN !, I get an error !!!! How ???
  feed_usingExplicitTypeClassInstance(AnimalTypeClass.CatInstance)(new CatFood())



  feed_usingImplicitTypeClassInstance(new CatFood)

}



trait Food {
  def eat(): Unit
}



trait AnimalTypeClass[AnimalInstance] {
  type FoodThatAnimalLikes <: Food
  def feed(f: FoodThatAnimalLikes) = f.eat()
}



object AnimalTypeClass {

  type Aux[Food, Animal] = AnimalTypeClass[Animal] {
    type FoodThatAnimalLikes = Food
  }

  implicit object CatInstance extends AnimalTypeClass[Cat] {
    override type FoodThatAnimalLikes = CatFood
  }

}


trait Cat

class CatFood extends Food {
  override def eat(): Unit = println("meow")
}

This is the error:

Error:(23, 38) could not find implicit value for parameter animalTypeClass: AnimalTypeClass.Aux[CatFood,AnimalInstance]
  feed_usingImplicitTypeClassInstance(new CatFood)

Error:(23, 38) not enough arguments for method feed_usingImplicitTypeClassInstance: (implicit animalTypeClass: AnimalTypeClass.Aux[CatFood,AnimalInstance])Unit.
Unspecified value parameter animalTypeClass.
  feed_usingImplicitTypeClassInstance(new CatFood)

EDIT:

If I insert the line:

AnimalTypeClass.CatInstance

before:

feed_usingImplicitTypeClassInstance(new CatFood)

then the code compiles again, even if the line

feed_usingExplicitTypeClassInstance(AnimalTypeClass.CatInstance)(new CatFood())

is commented out.

Mike Allen
  • 8,139
  • 2
  • 24
  • 46
jhegedus
  • 20,244
  • 16
  • 99
  • 167
  • 3
    Your explicit function call causes the `AnimalTypeClass` object to be instantiated, which creates the `implicit CatInstance`. Once you comment out that code, `AnimalTypeClass` is no longer instantiated, and so you no longer have an `implicit AnimalTypeClass` in scope, hence the error. The function call itself is irrelevant. – Mike Allen Apr 29 '18 at 21:32

2 Answers2

7

This is a pretty well known issue, where implicits which appear after their usage in the same file and without an explicit type annotation are not found. For that reason it is strongly advised (and this will eventually be enforced) to give all non-local implicits an explicit type annotation. Unfortunately implicit objects are a bit tricky here, because they always act like implicit definitions without type annotation, and it is impossible to give them an explicit type... However last I checked this seemed to be fixed in Dotty for implicit objects.

See also, among others https://github.com/scala/bug/issues/8697

The reason that it does work when you uncomment a call to AnimalTypeClass.CatInstance in your code is that that reference will force the implicit object to be type checked earlier, so its type will be known before its implicit usage.

Jasper-M
  • 14,966
  • 2
  • 26
  • 37
  • Many thanks for the answer, I'll go to sleep now, try to understand it tomorrow when my brain starts to work again. – jhegedus Apr 29 '18 at 20:24
  • Thanks again for the answer, so when you say, "its type will be known before its implicit usage" what do you mean by that? Known to whom? The compiler? So the compiler will know that a "type" for which it is looking for exists? Or an implicit object with that type exists ? Or the type itself ? What does it mean "implicit object to be type checked" ? Does it mean that the compiler associates (infers) a type with the implicit object (a value with a name) and when this happens it adds the implicit object to its dictionary that it uses to resolve implicits ? – jhegedus May 01 '18 at 10:17
  • 1
    In order for the compiler to know whether a certain implicit is eligible it needs to know its type. When a definition has no type annotation the compiler will have to process the entire body of that definition in order to infer its type. But the compiler cannot infer all types before starting the first implicit search because implicits can also influence type inference. Apparently that could cause some cyclic behavior in the inferencer. However when you explicitly refer to that definition the compiler will have to infer its type earlier in order to type the current expression where it's used. – Jasper-M May 01 '18 at 10:33
  • Hmmm, so "compiler cannot infer all types before starting the first implicit search" is the root of all this and "when you explicitly refer to that definition the compiler will have to infer its type earlier in order to type the current expression where it's used" means that the compiler "knows" what is the type of that implicit and then it becomes eligible to be used where it could be used. Many thanks, this is a great comment, explaining the situation. – jhegedus May 01 '18 at 20:07
5

You have the definition of the implicit value in the same file after the usage of this value. It is not initialized when the compiler looks for an implicit value when you call feed_usingImplicitTypeClassInstance. Calling feed_usingExplicitTypeClassInstance with an explicit reference to this implicit value forces the implicit to initialize, and the compiler can use it in the implicit call.

Possible solutions:

  • Move the definition of the implicit value to another file.
  • If the implicit value is in the same file, move its definition above the place where you use it implicitly.
Kolmar
  • 14,086
  • 1
  • 22
  • 25
  • Many thanks for the answer, to you too Kolmar, I'll go to sleep now, try to understand it tomorrow when my brain starts to work again. – jhegedus Apr 29 '18 at 20:25