Scala Естественная трансформация из DSL в функцию - PullRequest
0 голосов
/ 30 октября 2018

Я пытаюсь выполнить Естественное Преобразование между видами * -> * -> * поэтому хочу взять F[A, B] => G[A, B] В частности, я пытаюсь определить DSL, который затем можно преобразовать в фактические определения функций, поэтому MyDSL[A, B] => Function[A, B]

Вот определение естественной трансформации:

trait ~~>[F[_, _], G[_, _]] {
  def apply[A, B](fab: F[A, B]): G[A, B]
}

object ~~> {
  def apply[F[_, _], G[_, _]](implicit f2g: F ~~> G): F ~~> G = f2g
}

DSL выглядит так:

sealed trait MyDSL[A, B]

object MyDSL {

  case object Add1 extends MyDSL[Int, Int]
  case object Show extends MyDSL[Int, String]

  implicit def dsltoF: MyDSL ~~> Function = new ~~>[MyDSL, Function] {
    override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
      case Add1 => i => i + 1
      case Show => i => i.toString
    }
  }
}

Использование естественного преобразования напрямую работает нормально:

dsltoF(Add1)

вывод: res0: Function[Int,Int] = MyDSL$$anon$2$$Lambda$1816/700824958@6f3aa425

Это работает даже в случае, когда возвращаемой функцией является метод, принимающий 2 параметра типа.

Когда я пытаюсь определить объект DSL, который преобразуется с использованием универсального метода одного параметра типа, у него возникают проблемы.

case class Id[A]() extends MyDSL[A, A]

implicit def dsltoF: MyDSL ~~> Function = new ~~>[MyDSL, Function] {
    override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
      case Id() => identity[A] _
      case Add1 => i => i + 1
      case Show => i => i.toString
    }
  }

Я получаю found A required B ошибку компиляции. В этом случае Скала не распознает, что В - это А.

Я понимаю, почему параметры типа A & B не обязательно соотносятся должным образом с определением функции, которую я возвращаю, поэтому даже пишем: case Add1 => i => i + 1 в IntelliJ есть красные линии, так как он не понимает, что, хотя Add "является" MyDSL [Int, Int]. Хотя Скала в порядке с этим.

Параметры типа открыты для всех возможностей в сигнатуре метода apply в Natural Transformation, но в этом случае требуется какое-то ограничение. Я предполагаю, что поскольку в классе дел DSL нет значения для ограничения параметра типа, он сводится к сопоставлению с шаблоном, которое уже прошло, когда Scala интерпретирует сигнатуру метода, и поэтому оно ожидает другой тип B и лает.

Я, конечно, могу обойти это через .asInstanceOffness, но я имею в виду, давай.

Буду признателен за любые мысли о другой стратегии, чтобы заставить это работать.

1 Ответ

0 голосов
/ 30 октября 2018

Это известное ограничение в системе вывода типов в текущих версиях языка, которое должно быть снято в будущих версиях.

В этом случае вы можете использовать переменные типа в сопоставлении с образцом, чтобы обойти это ограничение:

import scala.language.higherKinds

trait ~~>[F[_, _], G[_, _]] {
  def apply[A, B](fab: F[A, B]): G[A, B]
}

object ~~> {
  def apply[F[_, _], G[_, _]](implicit f2g: F ~~> G): F ~~> G = f2g
}

sealed trait MyDSL[A, B]

object MyDSL {

  case class Id[A]() extends MyDSL[A, A]
  case class Const[A, B](constantResult: B) extends MyDSL[A, B]
  case object Add1 extends MyDSL[Int, Int]
  case object Show extends MyDSL[Int, String]

  implicit def dsltoF: MyDSL ~~> Function = new (MyDSL ~~> Function) {
    override def apply[A, B](fab: MyDSL[A, B]): Function[A, B] = fab match {
      case _: Id[x] => identity[x] _
      case c: Const[a, b] => (_ => c.constantResult)
      case Add1 => i => i + 1
      case Show => i => i.toString
    }
  }
}

По существу: если нет «места», где компилятор мог бы поместить более конкретную информацию о типе, просто передайте ему переменную типа в шаблоне, чтобы он мог присоединить к нему информацию о предполагаемом типе.

...