Как в scala заставить класс типов работать с шаблоном Aux?

Вот простой пример:

  trait Base {

    type Out
    def v: Out
  }

  object Base {

    type Aux[T] = Base { type Out = T }

    class ForH() extends Base {

      type Out = HNil

      override def v: Out = HNil
    }

    object ForH extends ForH
  }

  class TypeClass[B]

  trait TypeClassLevel1 {

    def summon[B](b: B)(implicit ev: TypeClass[B]): TypeClass[B] = ev
  }

  object TypeClass extends TypeClassLevel1 {

    implicit def t1: TypeClass[Base.Aux[HNil]] = new TypeClass[Base.Aux[HNil]]

    implicit def t2: TypeClass[Int] = new TypeClass[Int]
  }

  it("No Aux") {

    val v = 2

    TypeClass.summon(v) // works
  }


  it("Aux") {

    val v = new Base.ForH()

    TypeClass.summon(v) // oops
    TypeClass.summon(Base.ForH) // oops

    val v2 = new Base.ForH(): Base.Aux[HNil]
    TypeClass.summon(v2) // works!
  }

Очевидно, что объект Base / ForH имеет стабильный путь, что исключает возможность того, что компилятор не сможет разрешить тип ForH.Out.

Меня беспокоит не то, насколько неспособен компилятор определить тот факт, что ForH <:< Aux[HNil], а насколько легко исправить это, просто подняв простой тип (последние 2 строки). IMHO обе функции (лямбда типов и классы типов) являются важным аспектом функционального программирования, почему они не могут работать вместе одновременно?

Если вы знакомы с конструкцией компилятора, у меня возникнет дополнительный вопрос: что нужно сделать, чтобы улучшить алгоритм поиска по классам типов, чтобы это произошло? Большое спасибо за ваше мнение.

ОБНОВЛЕНИЕ 1: было предложено конкретное исправление, но у меня возникла другая проблема, пытаясь обобщить его, см. Как в scala заставить класс типов работать с шаблоном Aux? - Часть 2 для подробностей.


person tribbloid    schedule 22.01.2021    source источник
comment
TypeClass инвариантен в B, поэтому TypeClass[Base.Aux[HNil]] не является подтипом TypeClass[Base.ForH], ни наоборот.   -  person Jasper-M    schedule 23.01.2021


Ответы (2)


Таким образом, компилятор может вывести ForH <:< Aux[HNil], но (я не знаю точно почему) при разрешении неявного, когда возвращаемый тип использует лямбда-тип, он сбивается с толку, если вы не используете привязанный тип.

В любом случае это, вероятно, не лучшее объяснение, но, по крайней мере, я могу скомпилировать ваш код. Просто измените t1 на:

implicit def t1[T <: Base.Aux[HNil] ]: TypeClass[T] = new TypeClass[T]

это работает для меня в scastie с использованием Scala 2.13.4.

person Dan Simon    schedule 22.01.2021
comment
Да, это хорошая идея, но t2 работает нормально. Вы правы, у большинства типов классов должна быть граница. Я задержу вопрос на некоторое время, чтобы посмотреть, сможет ли его объяснить инсайдер компилятора. - person tribbloid; 22.01.2021
comment
Все инсайдеры компилятора закончили обучение :) Принято отдавать должное. Также обратитесь к части 2 для других объяснений: stackoverflow.com/questions/65853961/ - person tribbloid; 03.02.2021

Меня беспокоит не то, насколько компилятор неспособен понять, что ForH <:< Aux[HNil]

Конечно, компилятор видит, что Base.ForH <:< Base.Aux[HNil]. Вы можете проверить это

implicitly[Base.ForH <:< Base.Aux[HNil]]

компилирует.

IMHO обе функции (лямбда типов и классы типов) являются важным аспектом функционального программирования, почему они не могут работать вместе одновременно?

Почему вы говорите о лямбдах типа? Я не вижу лямбды типа в вашем вопросе.

Между прочим, лямбда-выражения типа не являются частью Scala 2, а ({ type λ[X] = ...F[X]... })#λ лямбда-выражения типа - это более или менее хитрость. Фактические лямбды типов добавлены в Scala 3.

val v = new Base.ForH() имеет тип Base.ForH (не Base.Aux[HNil] без преобразования вверх через присвоение типа val v = new Base.ForH(): Base.Aux[HNil] или ручную спецификацию типа val v: Base.Aux[HNil] = new Base.ForH()). TypeClass.summon(v) не следует компилировать, поскольку не существует неявного TypeClass[Base.ForH]. Что неявно вы бы рассматривали в качестве кандидата? TypeClass.t1? Но это не кандидат, вы можете проверить, что явно разрешено

TypeClass.summon(v)(TypeClass.t1)

не может компилироваться.

Что нужно сделать, чтобы улучшить алгоритм поиска по классам типов, чтобы это произошло?

Алгоритм неявного поиска не следует улучшать в этом конкретном месте. Он работает правильно, как и предполагалось.

Вы можете сделать класс типа контравариантным

class TypeClass[-B]

Тогда TypeClass.t1 будет кандидатом на TypeClass[Base.ForH] и TypeClass.summon(v) будет компилироваться.

В scala 2.13, как неявно использовать [значение singleton type ]?

person Dmytro Mitin    schedule 04.02.2021