#scala #functional-programming #typeclass
#scala #функциональное программирование #класс типов
Вопрос:
Я определил класс типов следующим образом:
/** Type class */
trait Drivable[V <: Vehicle]{
def drive(v:V)
}
Он запрашивает подтип транспортного средства в качестве параметра drive
функции. И теперь я пытаюсь использовать этот класс типов для Vehicle
самого.
/** ADT */
sealed trait Vehicle{
def drive[V <: Vehicle](implicit d: Drivable[V]): Unit = d.drive(this)
}
компилятор жалуется:
Error:(15, 74) type mismatch;
found : Vehicle.this.type (with underlying type Vehicle)
required: V
def drive[V <: Vehicle](implicit d: Drivable[V]): Unit = d.drive(this)
Это работает, если я принудительно приведу this
к V
def drive[V <: Vehicle](implicit d: Drivable[V]): Unit = d.drive(this.asInstanceOf[V])
Но я ненавижу это делать. Или это также работает, если я просто помещаю это в класс case:
final case class Car(name:String) extends Vehicle {
def drive(implicit d: Drivable[Car]): Unit = d.drive(this)
}
Но изображений у меня много case classes
, я должен повторить это в каждом.
Как правильно использовать этот класс типов в базовом признаке?
Комментарии:
1. Вместо этого добавьте
drive
в качестве метода расширения , или, если это просто для ADT , тогда в использовании typeclass на самом деле нет смысла. Компилятор прав насчет ошибки, вы говорите, что вам нужен экземплярDrivable[V]
для некоторых, которые еще не указаныV
, и вы передаете себя, нет способа доказать и гарантировать, что на сайте вызоваthis
является допустимым экземпляромV
; на самом деле довольно легко создать пример того, как это исправить.2. Может помочь, если вы сможете описать, какова ваша конечная цель и почему вы решили смоделировать ее таким образом, чтобы мы могли предложить другое решение или объяснить, где была ваша ошибка.
3. У меня их несколько
Vehicle
, например, я бы создал Car, Motorcycle … и так далее, у каждого естьdrive
метод, и, конечно, я бы предоставил реализации для каждого из них на сайте, но я не хочу повторять этот вызов в каждом классе case, поэтому я думаю поднять его до trait, каков правильный способ добиться этого?4. Я действительно не вижу, чем это будет отличаться от удаления класса типов и реализации
drive
для каждого класса. Или удалите методdrive
on Vehicle и оставьте метод расширения выполнять свою работу. Это также зависит от того, как вы хотите использовать свои экземпляры.5. Да, почему бы просто не использовать обычный способ ООП для выполнения задач?. Нужно
Vehicle
расширитьDrivable
, а затем реализоватьdrive
в каждом подклассе. Почемуdrive
нужно принимать транспортное средство в качестве параметра?
Ответ №1:
Либо создайте drive
метод расширения
sealed trait Vehicle
implicit class DrivableOps[V <: Vehicle](val vehicle: V) extends AnyVal {
def drive(implicit d: Drivable[V]): Unit = d.drive(vehicle)
}
или сделайте V
членом типа признака, а не параметром типа метода
sealed trait Vehicle {
type V >: this.type <: Vehicle
def drive(implicit d: Drivable[V]): Unit = d.drive(this)
}
case class Bus() extends Vehicle {
override type V = Bus
}
case class Car() extends Vehicle {
override type V = Car
}
Тип V
может быть сгенерирован автоматически:
//libraryDependencies = "com.github.dmytromitin" %% "auxify-macros" % "0.8"
import com.github.dmytromitin.auxify.macros.self
@self sealed trait Vehicle {
def drive(implicit d: Drivable[Self]): Unit = d.drive(this)
}
@self case class Bus() extends Vehicle
@self case class Car() extends Vehicle
Комментарии:
1. Интересно! это работает! однако
override
выглядит не очень красиво, но теоретически имеет смысл.2. И
implicit
один лучше. Спасибо @Dmytro