辅助模式适用于种类繁多的类型 [英] Aux Pattern for higher-kinded types
问题描述
这是问题的更简单表达,使用Foo
作为起作用的Aux
模式的示例:
Here is a much simpler formulation of the problem, using Foo
as an example of the Aux
pattern which does work:
// 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]
问题的旧提法
Old formulation of the question:
为下面令人费解的示例表示歉意.我本质上是想复制类型较高的类型的Aux
模式.
Apologies for the convoluted example below. I essentially want to duplicate the Aux
pattern for higher-kinded types.
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]]
}
这是我期望在最后一行中发生的事情:
This is what I expect to happen in that last line:
- 我们正在搜索
TestResult[Boolean]
-
testing
说,对于Wrapper
提供的某些Contr
,我们需要一个Contr[Boolean]
. -
Wrapper
给出Contr[_] = Foo.Aux[Int, String, ?]
的单个实例
- 因此编译器正在搜索
Foo.Aux[Int, String, Boolean]
-
Foo
提供了一个这样的实例
- 所以整个事情都会编译
- We're searching for a
TestResult[Boolean]
testing
says we need aContr[Boolean]
for someContr
provided byWrapper
Wrapper
gives a single instance ofContr[_] = Foo.Aux[Int, String, ?]
- So compiler is searching for a
Foo.Aux[Int, String, Boolean]
- There is a single such instance provided by
Foo
- So the whole thing compiles
这是我的build.sbt
,以防万一我遗漏某些东西:
Here is my build.sbt
in case I am missing something:
scalaVersion := "2.12.6"
scalacOptions := Seq(
"-language:existentials",
"-language:higherKinds",
"-Ypartial-unification", // EDIT
)
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.8")
推荐答案
这是我想出的一个解决方案:
Here is a solution I came up with:
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]
}
我不知道为什么为什么可以精确运行,但是A
和B
的自由度似乎允许编译器专注于正确地解决X[_]
以及约束的问题. A
和B
上的事件发生在不同的级别,因此我们最终实现了相同的功能.
I don't know why it works precisely, but it seems like the freedom of A
and B
allow the compiler to focus on resolving X[_]
properly, and then the constraints on A
and B
happen at a different level so we achieve the same functionality in the end.
这篇关于辅助模式适用于种类繁多的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!