Open scabug opened 10 years ago
Imported From: https://issues.scala-lang.org/browse/SI-8039?orig=1 Reporter: @paulp
@adriaanm said: The Plum lady in the wine cellar.
scala> trait Foo[CC[+X], +A]
warning: there were 1 feature warning(s); re-run with -feature for details
defined trait Foo
scala> trait FooChill { type CC[+X]; type A; type Apply = Foo[CC, A] }
warning: there were 1 feature warning(s); re-run with -feature for details
defined trait FooChill
scala> def x: (FooChill{type A = Int})#Apply = ???
x: Foo[_1.CC,Int] forSome { val _1: FooChill{type A = Int} }
@adriaanm said: Or, of course:
scala> def x[B]: (FooChill{type A = B})#Apply = ???
x: [B]=> Foo[_3.CC,B] forSome { val _3: FooChill{type A = B} }
@paulp said: Okay, that's cool and all, but do you have any good reason why should not be kind-polymorphic? Isn't it blazingly obvious what means in a ' -> expected' context? There's always going to be a manual translation available, and obviously you can't refer to things without names, so it's not like soundness is going to come into play. (Not that I think soundness is on anyone's mind.)
@adriaanm said: I'm not against simplifying this in 2.12, maybe overload _ in one more way. Given the workaround, type members are the better design when type applications are expected to be partial once in a while.
@retronym said: The proposal does seem to fit nicely with the new bound inference for _.
@paulp said: What? "When type applications are expected to be partial once in a while", have you thought this through? How are you supposed to know what the rest of the world is going to do with your types?
Not to mention it's disingenuous to suggest one is ever likely to have such freedom that one can hop between type members and type parameters. There are a billion things which constrain this choice - most of them are implementation bugs or inadequacies by design - never once will anyone be so free of those constraints that they'll be able to say "I'll make this a type member because I expect it to be partial once in a while."
@paulp said: Oh yes, and last but not least, this isn't even about partial application. It's about NO application. Partial application is a whole separate thing.
@adriaanm said: As with many of your outstanding tickets, it's not that I don't want to comment and fix, it's that I don't know how.
@paulp said: You know I can't resist such flattery. Okay, let's close this thing.
Hey wait a minute, "outstanding tickets" could mean "excellent tickets" or it could mean "the tickets which aren't closed yet." I'm going to infer the first meaning, no need for corrections.
Alexander Abdugafarov (frozenspider) said: Just ran into this issue recently, was very surprised. Workaround is nice and all, but I really think this should work with type parameters as well.
Any chance this will be fixed for Scala 2 or possibly Scala 3?
Scala 2, doubtful. Scala 3, https://dotty.epfl.ch/docs/reference/other-new-features/kind-polymorphism.html
Not sure if AnyKind
is related, but Paul's example does seem to work:
scala> trait Foo[CC[+X], +A]
// defined trait Foo
scala> def f[A](x: Foo[_, A]): A = ???
def f[A](x: Foo[?[+X], A]): A
scala> f(new Foo[List, Int]{})
scala.NotImplementedError: an implementation is missing
even more fixed-in-Dotty than I realized, then!
Not sure if
AnyKind
is related, but Paul's example does seem to work:scala> trait Foo[CC[+X], +A] // defined trait Foo scala> def f[A](x: Foo[_, A]): A = ??? def f[A](x: Foo[?[+X], A]): A scala> f(new Foo[List, Int]{}) scala.NotImplementedError: an implementation is missing
I tried to make that work with my own code but without much luck. This is ideally what I want:
sealed trait Expr[F[_], V, R, P] {
def visit[G[_]](v: Expr.Visitor[F, V, P, G]): G[R]
}
object Expr {
trait Visitor[F[_], V, P, G[_]] extends (Expr[F, V, *, P] ~> G) {
override final def apply[R](fa: Expr[F, V, R, P]): G[R] = fa.visit(this)
def visitNode[R](node: Expr.Node[F, V, R, P]): G[R]
// other visit Expr subclass methods...
}
// Expr subclasses....
}
import io.circe.Json
object ConvertExprToJson {
type Out[R] = Json
}
// Is it possible to remove these class type parameters too?
class ConvertExprToJson[F[_], V, P](
encodeSubNodes: Boolean = true
) extends Expr.Visitor[F, V, P, ConvertExprToJson.Out] {
// This signature doesn't compile.
private def encodeSubNode(name: String, subNodes: List[Expr[_, _, _, _]]): Json = {
if (encodeSubNodes) {
val subNodesJson = subNodes.map(_.visit(new ConvertExprToJson[_, _, _, _])).asJson
Json.obj(name -> subNodeJson)
}
else Json.obj()
}
// I currently work around this issue by using G[_] here
private def encodeNode[G[_]](node: Expr[G, _, _, _]): Json = {
Json.obj(
"className" -> node.getClass.getSimpleName.asJson
// encode common Expr fields...
)
}
override def visitNode[R](node: Expr.Node[F, V, R, P]): Json = {
encodeSubNode("subNodes" -> List(expr.subNode))).deepMerge(encodeNode(expr))
}
}
I think it's pretty reasonable to expect to be able to write a method like this:
If Thing's first type argument is kind *, you can. If it isn't, then god help you. There's no way most people will ever come up with this. The usual surrender probably involves adding an otherwise pointless type parameter to method f. This is not always an option and definitely shouldn't be mandated.
Here are a few things one might try before hitting upon the syntax which actually works.
Colonel Mustard in the conservatory: