可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm desperately trying to solve the following:
trait Access[Res[_]] { def access[C]: Res[C] }
trait CList[C1, A] extends Access[CList[_, A]] // ?!
def test[C1, C2, A](c: CList[C1, A]): CList[C2, A] = c.access[C2]
scalac just says: "error: illegal cyclic reference involving trait CList"
. how can I make this compile?
回答1:
You might be interested in type lambdas. The partial application you used in your answer is actually implemented in scalaz.
As the code tends to get less readable though, they started using type lambdas instead. The type in question could be written as
({type λ[α] = CList[α,A]})#λ
This works by creating a type projection on a parameterized type λ
inside a structural type thus capturing the outer type parameter (in this case A
).
The other problem concerning variance described in your answer could be solved by making the Res
parameter in Access
covariant.
After these changes your code should look like this:
trait Access[+Res[_]] { def access[C] : Res[C]}
trait CList[C, +A] extends Access[({type λ[α] = CList[α,A]})#λ]
回答2:
googling for "partial type application" i found this solution posted by James Iry on the scala debate list ( http://scala-programming-language.1934581.n4.nabble.com/Partial-type-inference-td2007311.html ; adapted so the arg order is changed):
type Partial2[T[_,_], B] = {
type Apply[A] = T[A,B]
}
trait CList[C1, A] extends Access[Partial2[CList, A]#Apply]
cheese louise, is this really the only way to do that in scala in 2011 ?!!
EDIT:
This fails with covariance in A
:,-(
trait Access[Res[_]] { def access[C]: Res[C] }
type Partial2[T[_,_], B] = {
type Apply[A] = T[A,B]
}
trait CList[C1, +A] extends Access[Partial2[CList, A]#Apply]
"covariant type A occurs in invariant position"
回答3:
Just to update things
add this compiler plugin to your sbt for kind projection and you'll get a nice syntax using ?
.
This removes the type projection boilerplate which looks messy!
So you can write stuff like Either[String, ?]
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.7")
it's implemented with the same old type projection underneath
You can also find it here:
https://underscore.io/blog/posts/2016/12/05/type-lambdas.html
回答4:
I know this is a really old question, but anyway:
trait AnyAccess {
type Res[X]
def access[Z]: Res[Z]
}
trait AnyCList extends AnyAccess { me =>
type C
type A
// this could be a subtype bound instead, if needed
type Res[X] = AnyCList { type C = X; type A = me.A }
}
case object AnyCList {
type of[C0, +A0] = AnyCList { type C = C0; type A <: A0 }
}
case object buh {
def test[C1, C2, A](c: AnyCList.of[C1, A]): AnyCList.of[C2, A] = c.access[C2]
}
回答5:
Here's a method that worked for me to "partially apply type parameters":
I had a function like
def foo[A, B, C, D, E](...)
Such that I needed to hint only one type parameter for the compiler to infer the rest. This worked for me:
object InferType {
type InferType[A] = Option[A]
def apply[A]: Option[A] = None
}
Update foo to take an additional parameter of type InferType:
// t parameter is unused in implementation but
// is used by compiler to infer other type parameters
def foo[A, B, C, D, E](..., t: InferType[D])
Usage:
foo(..., InferType[ConcreteD])