Scala, метод переопределения для типа generi c - PullRequest
1 голос
/ 07 января 2020

Я пытаюсь создать DSL , один из методов этого DSL не имеет параметров и использует ограниченный тип generi c. Сегодня я должен добавить «функцию», которая в идеале будет использовать то же имя метода. Однако, поскольку единственным параметром является параметр generi c, я не могу переопределить его обычным способом.

Есть ли хитрость, позволяющая использовать один и тот же метод для разных типов generi c?

Мой метод выглядит следующим образом:

def ask[H <: Handler] = {
  new CommandBuilder[H]
}
class CommandBuilder[H <: Handler] {
  def toExecute[C <: H#C](command: C) = {
    //...
  }
} 

И я хотел бы добавить:

def ask[S <: State] = {
  new QueryBuilder[S]
}
class QueryBuilder[S <: State] {
  def toExecute[Q <: S#Q](query: Q) = {
    //...
  }
} 

Я думал сопоставить шаблон с ClassTag для типа, но я нужен сильный тип безопасности:

  • Query на Handler, не допускается. ask [State] должен вернуть QueryBuilder
  • Command и Query являются единственными поддерживаемыми типами. Тип c обобщенного типа ask может быть только Handler или State.

1 Ответ

1 голос
/ 07 января 2020

Может быть, вы могли бы изменить свой код на что-то вроде этого?

sealed trait FooBar

sealed trait Foo extends FooBar {
  def process(i: Int): Int
}

object Foo {
  implicit final case object FooImpl extends Foo {
    override def process(i: Int): Int = i + 1
  }
}

sealed trait Bar extends FooBar {
  def process(s: String): String
}

object Bar {
  implicit final case object BarImpl extends Bar {
    override def process(s: String): String = s.toUpperCase
  }
}

object Test {
  trait FooBarPartiallyApplied[FB <: FooBar] {
    type Out
    def out: Out
  }

  object FooBarPartiallyApplied {
    type Aux[FB <: FooBar, _Out] = FooBarPartiallyApplied[FB] { type Out = _Out }

    implicit final def FooPartiallyAppliedBuilder[F <: Foo]: Aux[F, FooPartiallyApplied[F]] =
      new FooBarPartiallyApplied[F] {
        override final type Out = FooPartiallyApplied[F]

        override final val out: FooPartiallyApplied[F] =
          new FooPartiallyApplied[F](dummy = true)
      }

    implicit final def BarPartiallyAppliedBuilder[B <: Bar]: Aux[B, BarPartiallyApplied[B]] =
      new FooBarPartiallyApplied[B] {
        override final type Out = BarPartiallyApplied[B]

        override final val out: BarPartiallyApplied[B] =
          new BarPartiallyApplied[B](dummy = true)
      }

    final class FooPartiallyApplied[F <: Foo](private val dummy: Boolean) extends AnyVal {
      def toExecute(i: Int)(implicit foo: F): Int = foo.process(i)
    }

    final class BarPartiallyApplied[B <: Bar](private val dummy: Boolean) extends AnyVal {
      def toExecute(s: String)(implicit bar: B): String = bar.process(s)
    }
  }

  def ask[FB <: FooBar](implicit pa: FooBarPartiallyApplied[FB]): pa.Out =
    pa.out
}

Это работает как ожидалось:

Test.ask[Foo.FooImpl.type].toExecute(10)
// res: Int = 11

Test.ask[Foo.FooImpl.type].toExecute("blah")
// Type error.

Test.ask[Bar.BarImpl.type].toExecute(10)
// Type error.

Test.ask[Bar.BarImpl.type].toExecute("blah")
// res: String = "BLAH"
...