При использовании HList с GADT мне приходится использовать asInstanceOf[H]. Есть ли способ избежать броска?

Учитывая 2 алгебры GADT, которые знают друг о друге, и 2 взаимно рекурсивных интерпретатора, у меня возникают проблемы с приведением типа A к типу h <: HList, хотя в контексте сопоставления шаблона следует подразумевать, что тип A это тип ч.

Есть ли способ избежать вызова asInstanceOf[h] в интерпретаторе?

  abstract class KvpHList[H<:HList]
  object KvpNil extends KvpHList[HNil]
  case class KvpCons[H <: A :: T,A, T<:HList](head: KvpValue[A], tail: KvpHList[T])(implicit isHCons: IsHCons.Aux[H,A,T]) extends KvpHList[H] {
    val hCons: IsHCons.Aux[H,A,T] = isHCons
  }

  abstract class KvpValue[A]
  case object StringData extends KvpValue[String]
  case class HListData[H <:HList](member: KvpHList[H]) extends KvpValue[H]

  def hListInterpreter[H<:HList](hList: KvpHList[H]): H => String = {
      hList match {
        case KvpNil => (hNil: H) => "Nil"
        case cons: KvpCons[H,a,t]=> {
          implicit val hCons = cons.hCons
          (input: H) => {
            s"${kvpInterpreter(cons.head)(input.head)} :: ${hListInterpreter(cons.tail)(input.tail)}"
          }
        }
      }
  }

  def kvpInterpreter[A](kvpValue: KvpValue[A]): A => String = {
    kvpValue match {
      case StringData => (str: String) => str
      case h: HListData[h] => {
        (input: A) => {                               // tried (input: h) as well
          val toString: h => String = hListInterpreter(h.member)
          toString(input.asInstanceOf[h])             // <--- CASTING :(
        }
      }
    }
  }

  kvpInterpreter(HListData(KvpCons(StringData, KvpNil))).apply("Hello" :: HNil)
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
91
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку H в KvpCons однозначно определяется A и T, KvpCons можно параметризовать двумя параметрами типа, а не тремя.

Сопоставление шаблонов на уровне типов — это классы типов.

  abstract class KvpHList[H <: HList]
  object KvpNil extends KvpHList[HNil]
  case class KvpCons[A, T <: HList](head: KvpValue[A], tail: KvpHList[T]) extends KvpHList[A :: T]

  abstract class KvpValue[A]
  case object StringData extends KvpValue[String]
  case class HListData[H <: HList](member: KvpHList[H]) extends KvpValue[H]

  trait HListInterpreter[H <: HList] {
    def apply(hList: KvpHList[H]): H => String
  }

  object HListInterpreter {
    implicit val nil: HListInterpreter[HNil] = new HListInterpreter[HNil] {
      override def apply(hList: KvpHList[HNil]): HNil => String = _ => "Nil"
    }

    implicit def cons[A, T <: HList](implicit
                                     headKvpInterpreter: KvpInterpreter[A],
                                     tailHListInterpreter: HListInterpreter[T]
                                    ): HListInterpreter[A :: T] = new HListInterpreter[A :: T] {
      override def apply(hList: KvpHList[A :: T]): A :: T => String = hList match {
        case cons: KvpCons[_, _] => input => s"${headKvpInterpreter(cons.head)(input.head)} :: ${tailHListInterpreter(cons.tail)(input.tail)}"
      }
    }
  }

  def hListInterpreter[H <: HList](hList: KvpHList[H])(implicit hListInterp: HListInterpreter[H]): H => String = hListInterp(hList)

  trait KvpInterpreter[A] {
    def apply(kvpValue: KvpValue[A]): A => String
  }

  object KvpInterpreter {
    implicit val string: KvpInterpreter[String] = new KvpInterpreter[String] {
      override def apply(kvpValue: KvpValue[String]): String => String = str => str
    }

    implicit def hList[H <: HList : HListInterpreter]: KvpInterpreter[H] = new KvpInterpreter[H] {
      override def apply(kvpValue: KvpValue[H]): H => String = kvpValue match {
        case h: HListData[H] => input => {
          val toString: H => String = hListInterpreter(h.member)
          toString(input)
        }
      }
    }
  }

  def kvpInterpreter[A](kvpValue: KvpValue[A])(a: A)(implicit kvpInterp: KvpInterpreter[A]): String = kvpInterp(kvpValue)(a)

Можете ли вы объяснить, что вы подразумеваете под «сопоставлением шаблонов на уровне типов - это классы типов».

Travis Stevens 29.05.2019 20:21

Мои HListInterpreter и KvpInterpreter — это классы шрифтов. Когда вы пишете hList match { case KvpNil => ???; case KvpCons(h, t) => ???}, вы сопоставляете hList с шаблонами во время выполнения. Когда вы пишете implicit val nil: HListInterpreter[HNil] = ???; implicit def cons... : HListInterpreter[A :: T] = ???, вы как бы сопоставляете тип H с типами HNil, A :: T во время компиляции. Когда вы выполняли сопоставление шаблонов во время выполнения, вам не хватало информации о типах (поэтому вам приходилось выполнять приведение типов).

Dmytro Mitin 29.05.2019 20:37

Почему я теряю информацию о типах только с типами HList? При сопоставлении со StringData компилятор, похоже, знал, что тип A является строкой.

Travis Stevens 29.05.2019 21:28

Я думаю, из-за стирания типа в дженериках.

Dmytro Mitin 29.05.2019 21:36

Другие вопросы по теме