Тип GADT как бесформенный копроцесс - как построить интерпретатор с произвольным числом алгебр - PullRequest
2 голосов
/ 06 марта 2020

Допустим, у меня есть два типа GADT.

  abstract class Numbers[A]()
  case class IntType() extends Numbers[Int]

  abstract class Letters[A]()
  case class EnglishType() extends Letters[String]

И у меня есть интерпретатор для каждого из типов GADT, который распечатает описание для каждого из подтипов GADT.

  trait Interpreter[ALG[_],A] {
    def description(a: ALG[A]) : String
  }

  case class NumbersInterpreter[A]() extends Interpreter[Numbers,A] {
    override def description(a: Numbers[A]): String =
      a match {
        case i: IntType => "Int"
      }
  }

  case class LettersInterpreter[A]() extends Interpreter[Letters,A] {
    override def description(a: Letters[A]): String =
      a match {
        case e: EnglishType => "English"
      }
  }

Я хочу объединить два GADT в один GADT с именем All

  type All[A] = Numbers[A] :+: Letters[A] :+: CNil

. Я могу создать новый интерпретатор, жестко закодировав все значения GADT.

  case class DualInterpreter[A](
    numbersInterpreter: NumbersInterpreter[A],
    lettersInterpreter: LettersInterpreter[A]) extends Interpreter[All,A] {
    override def description(a: All[A]): String =
      a match {
        case Inl(num) => numbersInterpreter.description(num)
        case Inr(Inl(let)) => lettersInterpreter.description(let)
        case _ => sys.error("Unreachable Code")
      }
  }

Однако я хотел бы добавить группу алгебр и интерпретаторов GADT и произвольно объединить их в одну алгебру, поэтому я ищу более общий c подход для замены DualInterpreter выше. Я вижу, что сигнатура типа - это что-то вроде

  case class ArbitraryInterpreter[ALG[_]<:Coproduct,A](???) extends Interpreter[ALG,A] {
    override def description(a: ALG[A]): String = ???
  }

. Главное, что я хотел бы абстрагировать, - это сопоставление с шаблоном внутри метода description, поскольку оно может быть довольно уродливым с числом. доступных алгебр. Будет иметься интерпретатор, где аргументы конструктора являются интерпретаторами, а делегаты сопоставления с образцом соответствующему интерпретатору на основе типа ALG, переданного в метод описания.

1 Ответ

1 голос
/ 06 марта 2020

Вы имеете в виду что-то вроде этого?

// using kind projector
def merge[L[_], R[_] <: Coproduct, A](
  li: Interpreter[L, A],
  ri: Interpreter[R, A]
): Interpreter[Lambda[A => L[A] :+: R[A]] , A] =
  new Interpreter[Lambda[A => L[A] :+: R[A]] , A] {
    override def description(lr: L[A] :+: R[A]): String =
      lr match {
        case Inl(l) => li.description(l)
        case Inr(r) => ri.description(r)
      }
  }

возможно с

implicit class InterpreterOps[L[_], A](val l: Interpreter[L, A]) extends AnyVal {

  def ++ [R[_] <: Coproduct](r: Interpreter[R, A]): Interpreter[Lambda[A => L[A] :+: R[A]] , A] = merge(l, r)
}

, используемым как


type CNilF[A] = CNil // trick to make things consistent on kind-level
case class CNilInterpreter[A]() extends Interpreter[CNilF, A] {
  override def description(a: CNilF[A]): String = ???
}

def allInterpreter[A]: Interpreter[All, A] =
  // :+: is right associative, but normal methods are left associative,
  // so we have to use parens
  NumbersInterpreter[A]() ++ (LettersInterpreter[A]() ++ CNilInterpreter[A]())
...