Странная ошибка компилятора Scala при удалении вызова функции с возвращаемым типом Unit, как это вообще возможно?
Вот странная ситуация:
Если я закомментирую звонок feed_usingExplicitTypeClassInstance
ниже, то я получаю ошибку компилятора.
Очень загадочно. Любое объяснение?
Я имею в виду, я закомментирую вызов функции (которая не возвращает значения), а затем код больше не компилируется?
Должно ли это вообще быть вообще возможным в теории? На каком языке программирования?
Я имею в виду, я закомментирую что-то вроде println("hello")
а потом код больше не компилируется?
Конечно, было бы понятно, если бы я закомментировал объявление или что-то еще, но вызов функции, которая ничего не возвращает?
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")
}
Это ошибка:
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)
РЕДАКТИРОВАТЬ:
Если я вставлю строку:
AnimalTypeClass.CatInstance
до:
feed_usingImplicitTypeClassInstance(new CatFood)
затем код компилируется снова, даже если строка
feed_usingExplicitTypeClassInstance(AnimalTypeClass.CatInstance)(new CatFood())
закомментировано.
2 ответа
У вас есть определение неявного значения в том же файле после использования этого значения. Он не инициализируется, когда компилятор ищет неявное значение при вызове feed_usingImplicitTypeClassInstance
, призвание feed_usingExplicitTypeClassInstance
с явной ссылкой на это неявное значение заставляет неявное инициализироваться, и компилятор может использовать его в неявном вызове.
Возможные решения:
- Переместите определение неявного значения в другой файл.
- Если неявное значение находится в том же файле, переместите его определение выше того места, где вы его неявно используете.
Это довольно известная проблема, когда не обнаруживаются последствия, которые появляются после их использования в том же файле и без явной аннотации типа. По этой причине настоятельно рекомендуется (и это в конечном итоге будет применено) дать всем нелокальным последствиям явную аннотацию типа. К сожалению, неявные объекты здесь немного сложны, потому что они всегда действуют как неявные определения без аннотации типа, и невозможно дать им явный тип... Однако в прошлом я проверял, что это было исправлено в Dotty для неявных объектов.
Смотрите также, среди прочего, https://github.com/scala/bug/issues/8697
Причина, по которой это работает, когда вы раскомментируете AnimalTypeClass.CatInstance
в вашем коде эта ссылка заставит неявный объект быть проверенным по типу ранее, поэтому его тип будет известен до неявного использования.