EDIT : Voici une formulation beaucoup plus simple du problème, en utilisant Foo
comme un exemple de la Aux
modèle qui fait travail :
// 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]
Ancienne formulation de la question :
Toutes mes excuses pour l'exemple alambiqué ci-dessous. Je veux essentiellement dupliquer le Aux
pour les types supérieurs.
Le 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]]
}
C'est ce que j'attends de cette dernière ligne :
- Nous sommes à la recherche d'un
TestResult[Boolean]
-
testing
dit que nous avons besoin d'unContr[Boolean]
pour certainsContr
fourni parWrapper
-
Wrapper
donne une seule instance deContr[_] = Foo.Aux[Int, String, ?]
- Donc le compilateur cherche un
Foo.Aux[Int, String, Boolean]
- Il existe une seule instance de ce type fournie par
Foo
- Donc le tout se compile
Voici mon build.sbt
au cas où je manquerais quelque chose :
scalaVersion := "2.12.6"
scalacOptions := Seq(
"-language:existentials",
"-language:higherKinds",
"-Ypartial-unification", // EDIT
)
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.8")