Skip to content

Ignoring a type constructor requires rocket science #8039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
scabug opened this issue Dec 6, 2013 · 16 comments
Open

Ignoring a type constructor requires rocket science #8039

scabug opened this issue Dec 6, 2013 · 16 comments
Labels
enhancement fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) typer
Milestone

Comments

@scabug
Copy link

scabug commented Dec 6, 2013

I think it's pretty reasonable to expect to be able to write a method like this:

def f[A](xs: Thing[_, A]): A = xs.bippy

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.

scala> trait Foo[CC[+X], +A]
defined trait Foo

scala> trait Foo[CC[+X], +A] ; def f[A](x: Foo[_, A]): A = ???
<console>:8: error: _$1 takes no type parameters, expected: one
       trait Foo[CC[+X], +A] ; def f[A](x: Foo[_, A]): A = ???
                                               ^

// That's not what you said about _$1 a minute ago, you sadist.
scala> trait Foo[CC[+X], +A] ; def f[A](x: Foo[_[_], A]): A = ???
<console>:8: error: _$1 does not take type parameters
       trait Foo[CC[+X], +A] ; def f[A](x: Foo[_[_], A]): A = ???
                                               ^
// If you're lucky enough to come up with the existential syntax for
// higher order type parameters, you will still be burned if you get the
// variance wrong. Remember we're doing all this just to IGNORE IT.
scala> trait Foo[CC[+X], +A] ; def f[A](x: Foo[CC forSome { type CC[X] }, A]): A = ???
<console>:8: error: kinds of the type arguments (CC forSome { type CC[X] <: Any },A) \
do not conform to the expected kinds of the type parameters \
(type CC,type A) in trait Foo.
CC forSome { type CC[X] <: Any }'s type parameters do not match \
type CC's expected parameters:
type X is invariant, but type X (in trait Foo) is declared covariant
       trait Foo[CC[+X], +A] ; def f[A](x: Foo[CC forSome { type CC[X] }, A]): A = ???
                                           ^

Colonel Mustard in the conservatory:

// Nice, three feature warnings for the only way to write it.
scala> trait Foo[CC[+X], +A] ; def f[A](x: Foo[CC forSome { type CC[+X] }, A]): A = ???
warning: there were 3 feature warning(s); re-run with -feature for details
defined trait Foo
f: [A](x: Foo[CC forSome { type CC[+X] <: Any },A])A
@scabug
Copy link
Author

scabug commented Dec 6, 2013

Imported From: https://issues.scala-lang.org/browse/SI-8039?orig=1
Reporter: @paulp

@scabug
Copy link
Author

scabug commented Dec 6, 2013

@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} }

@scabug
Copy link
Author

scabug commented Dec 6, 2013

@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} }

@scabug
Copy link
Author

scabug commented Dec 6, 2013

@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.)

@scabug
Copy link
Author

scabug commented Dec 10, 2013

@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.

@scabug
Copy link
Author

scabug commented Dec 10, 2013

@retronym said:
The proposal does seem to fit nicely with the new bound inference for _.

https://github.com/retronym/scala/tree/ticket/8039

@scabug
Copy link
Author

scabug commented Dec 11, 2013

@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."

@scabug
Copy link
Author

scabug commented Dec 11, 2013

@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.

@scabug
Copy link
Author

scabug commented Feb 20, 2015

@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.

@scabug
Copy link
Author

scabug commented Feb 20, 2015

@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.

@scabug
Copy link
Author

scabug commented Nov 1, 2015

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.

@jeffmay
Copy link

jeffmay commented Dec 8, 2020

Any chance this will be fixed for Scala 2 or possibly Scala 3?

@SethTisue SethTisue added this to the Backlog milestone Dec 8, 2020
@SethTisue
Copy link
Member

@SethTisue SethTisue added the fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) label Dec 8, 2020
@Jasper-M
Copy link

Jasper-M commented Dec 8, 2020

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

@SethTisue
Copy link
Member

even more fixed-in-Dotty than I realized, then!

@jeffmay
Copy link

jeffmay commented Dec 9, 2020

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))
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) typer
Projects
None yet
Development

No branches or pull requests

4 participants