辅助模式适用于种类繁多的类型

时间:2018-09-30 20:42:56

标签: scala type-level-computation higher-kinded-types

编辑:这是一个更简单的问题提法,使用Foo作为起作用Aux模式的示例:

// Foo is a simple Aux-pattern type
trait Foo[A, B] { type Out }

object Foo {
  type Aux[A, B, C] = Foo[A, B] { type Out = C }
  // One instance, turning Int+String into Boolean
  implicit val instance: Foo.Aux[Int, String, Boolean] = null
}

// Wrapper is exactly the same but contains a higher-kinded type
trait Wrapper[A, B] { type Contract[_] }

object Wrapper {
  type Aux[A, B, C[_]] = Wrapper[A, B] { type Contract[_] = C[_] }
  // One instance, linking Int + String to Option
  implicit val instance: Wrapper.Aux[Int, String, Option] = null
}

// Same test for both
def fooTest[A, B, C](implicit ev: Foo.Aux[A, B, C]): C = ???
def wrapperTest[X[_]](implicit ev: Wrapper.Aux[Int, String, X]): X[Boolean] = ???

// Compiles as expected
fooTest: Boolean

// Does not compile: could not find implicit value for parameter ev: Wrapper.Aux[Int,String,X]
wrapperTest: Option[Boolean]

// Does compile:
wrapperTest(implicitly[Wrapper.Aux[Int, String, Option]]): Option[Boolean]

问题的旧提法

以下复杂例子的道歉。我本质上是想复制类型较高的类型的Aux模式。

scala:

// Foo is a normal Aux pattern calculation
trait Foo[A, B] { type Out }

object Foo {
  type Aux[A, B, C] = Foo[A, B] { type Out = C }
  // Foo turns Int + String into Boolean
  implicit val intInstance: Foo.Aux[Int, String, Boolean] = null
}

// Wrapper is supposed to be a type-level computation across
// type-level functions
// It takes two types and binds them with a contract (a nested
// type-level function)
trait Wrapper[A, B] { type Contract[X] }

object Wrapper {
  type Aux[A, B, C[_]] = Wrapper[A, B] { type Contract[X] = C[X] }

  // It has one instance: It binds Int and String to the type-level
  // function Foo.
  implicit val fooWrapper: Wrapper.Aux[Int, String, Foo.Aux[Int, String, ?]] = null

}

object Testing {

  trait TestResult[X]

  // We summon a Contr, which is provided by Wrapper
  // The idea is we get the result of Foo's computation without summoning
  // Foo explicitly. This allows us to easily swap Foo out for another
  // Function if we desire
  implicit def testing[A, B, Contr[_], X](
    implicit wrapper: Wrapper.Aux[A, B, Contr],
    foo: Contr[X]
  ): TestResult[X] = ???


  // Compiles as expected
  implicitly[Wrapper.Aux[Int, String, Foo.Aux[Int, String, ?]]]
  implicitly[Wrapper[Int, String]]
  implicitly[Foo.Aux[Int, String, Boolean]]
  implicitly[Foo[Int, String]]
  val result1: TestResult[Boolean] = testing[Int, String, Foo.Aux[Int, String, ?], Boolean]

  // Does not compile
  val result2: TestResult[Boolean] = testing
  implicitly[TestResult[Boolean]]
}

这是我期望在最后一行中发生的事情:

  • 我们正在搜索TestResult[Boolean]
  • testing说,我们需要Contr[Boolean]提供的某些Contr的{​​{1}}
  • Wrapper给出Wrapper的单个实例
  • 因此编译器正在搜索Contr[_] = Foo.Aux[Int, String, ?]
  • Foo.Aux[Int, String, Boolean]提供了一个这样的实例
  • 所以整个事情都会编译

这是我的Foo,以防万一我遗漏某些东西:

build.sbt

2 个答案:

答案 0 :(得分:1)

您想出了一个有趣的解决方法,但最初的问题确实是一个错误:scala/scala#6573,已在Scala 2.13:{{3}}中修复。不幸的是,由于它改变了类型推断的工作方式,因此它不能反向移植到2.12,这是编译器的精妙部分,甚至没有特别说明。但是您可以使用Scala 2.13.0-M4或2.13.0-M5进行试用。

答案 1 :(得分:0)

这是我想出的解决方案:

trait Wrapper[A, B] { type Contract[_] }

object Wrapper {
  type Aux[A, B, C[_]] = Wrapper[A, B] { type Contract[_] = C[_] }
  // One instance, linking Int + String to Option
  implicit def instance[A, B](implicit ev1: A =:= Int, ev2: B =:= String): Wrapper.Aux[A, B, Option] = null

}

object Testing {

  def wrapperTest[A, B, X[_]](implicit ev: Wrapper.Aux[A, B, X]): X[Boolean] = ???

  // These compile now!!
  wrapperTest
  wrapperTest: Option[Boolean]

  // Do NOT compile, as expected
  // wrapperTest[Boolean, Char, Option]: Option[Boolean]
  // wrapperTest[Int, String, List]: Option[Boolean]

}

我不知道为什么为什么确实可以正常工作,但是AB的自由性似乎允许编译器专注于解决X[_]正确地进行操作,然后对AB的约束发生在不同的级别,因此我们最终实现了相同的功能。