Skip to content
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

Type aliases with return type inference works bad #8740

Open
scabug opened this issue Jul 21, 2014 · 12 comments
Open

Type aliases with return type inference works bad #8740

scabug opened this issue Jul 21, 2014 · 12 comments
Labels
Milestone

Comments

@scabug
Copy link

scabug commented Jul 21, 2014

Example below shows the problem:

trait SomeTag
type @@[+A, T] = A with T
def withSomeTag[A](a: A): A @@ SomeTag  = a.asInstanceOf[A @@ SomeTag]
// def need(a: String @@ SomeTag) = ??? // <- it works
// but this not:
type TypeAlias = String @@ SomeTag
def need(a: TypeAlias) = ???
need(withSomeTag("foo"))

Causes:

<console>:13: error: type mismatch;
 found   : String("foo")
 required: SomeTag with String
              need(withSomeTag("foo"))

Changing parameter's type to unaliased version works fine.
Because of this bug, using of scalaz.Tag / shapeless.tag.Tagged is difficult.

@scabug
Copy link
Author

scabug commented Jul 21, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8740?orig=1
Reporter: Arek Burdach (ark_adius)
Affected Versions: 2.11.1

@scabug
Copy link
Author

scabug commented Jul 22, 2014

@paulp said:
Similar to #8709, but with reduced opportunity to deny that it's a bug.

@scabug
Copy link
Author

scabug commented Jul 24, 2014

@retronym said:
This is an interesting one. I was the same corners of subtyping recently to investigate a problem with refinement types with an existential as one of the parents. My patch for that didn't solve this problem.

But I pushed on to analyze why this fails. See the comments in the test case here: https://github.com/retronym/scala/compare/scala:2.11.x...ticket/8740?expand=1

That branch also sketches out a fix, in which I reorder the descent into a refinement's parent types on the LHS to avoid overconstraining type variables.

@scabug
Copy link
Author

scabug commented Mar 29, 2016

@milessabin said:
Any chance of this going in for 2.12.0-M4?

@scabug
Copy link
Author

scabug commented Mar 29, 2016

@retronym said:
M4 is already a bit late, so we're planning to ship in the next few days once we have SAM support merged. M5 would be possible, though.

I was never all that happy with my solution, it seemed very arbitrary.

Maybe this version would be a fraction less ugly.

  def check = parents exists (retry(_, tp2))
  if (tp.isGround)  check
  else suspendingTypeVars(typeVarsInType(tp))(check) else check

@scabug
Copy link
Author

scabug commented Aug 20, 2016

@benhutchison said:
It'd be great to see this fixed. I was wondering why I hadn't hit it myself, which lead me to a partial workaround:

In the failing example, TypeAlias is an alias that refers to another alias @@. Ive had success using aliases for tagged types, but Ive always defined directly in terms of unaliased types, eg

type Distance = Int with TagAs.Tagged[DistanceObj.type]

object TagAs {
  trait Tagged[+U]
}

That is, can't stack 2 aliases over a tagged type, but 1 seems OK.

@scabug
Copy link
Author

scabug commented Nov 2, 2016

@cvogt said:
Jason, did the expected fix end up not being a full solution?

@scabug
Copy link
Author

scabug commented Nov 14, 2016

B. Tommy Jensen (tommy-at-grindvoll.com) said:
I just bumped into this one today. Same behavior with 2.12.0. Any chance that a fix will be included in the near future?

@joroKr21
Copy link
Member

Tweak the example a bit and I suspect the outlined fix breaks down again:

trait SomeTag
type @@[+A, T] = A with T
def withSomeTag[A, B](a: A): A @@ B  = a.asInstanceOf[A @@ B]
// def need(a: String @@ SomeTag) = ??? // <- it works
// but this not:
type TypeAlias = String @@ SomeTag
def need(a: TypeAlias) = ???
need(withSomeTag("foo"))

Now we have type variables in both parents of the refined type (not uncommon if we're defining an implicit typeclass instance for any tag) and isGround changes nothing.

@joroKr21
Copy link
Member

This works though:

trait Tagged[T]
trait SomeTag
type @@[+A, T] = Tagged[T] with A
def withSomeTag[A, B](a: A): A @@ B  = a.asInstanceOf[A @@ B]
// def need(a: String @@ SomeTag) = ??? // <- it works
// but this not:
type TypeAlias = String @@ SomeTag
def need(a: TypeAlias) = ???
need(withSomeTag("foo"))

@joroKr21
Copy link
Member

^ Sorry, now it infers the correct types, but the erasure is wrong 😩

@joroKr21
Copy link
Member

A workaround that works:

object Main {
  trait SomeTag
  type @@[+A, T] <: A with T
  def withSomeTag[A, B](a: A): A @@ B  = a.asInstanceOf[A @@ B]
  // def need(a: String @@ SomeTag) = ??? // <- it works
  // but this not:
  type TypeAlias = String @@ SomeTag
  def need(a: TypeAlias) = ???
  need(withSomeTag("foo"))
}

The only downside is that primitives will be boxed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants