Skip to content

Match typing with trait types doesn't work #8493

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

Closed
travisbrown opened this issue Mar 10, 2020 · 4 comments
Closed

Match typing with trait types doesn't work #8493

travisbrown opened this issue Mar 10, 2020 · 4 comments

Comments

@travisbrown
Copy link
Contributor

minimized code

trait Foo; trait Bar

type Res[X] = X match {
  case Foo => String
  case Bar => Int
}

0: Res[Bar]

Compilation output

1 |0: Res[Bar]
  |^
  |Found:    (0 : Int)
  |Required: Res[Bar]

expectation

It works as expected if you change the traits to classes, or if you reverse the order of the cases.

@OlivierBlanvillain
Copy link
Contributor

OlivierBlanvillain commented Mar 11, 2020

That's working as intended. Reducing Res[Bar] to Int would be unsound, and once we merge #8024 it's going to be trivial to make a counter example. Given the Res type you defined above, the following typechecks (after #8024):

def res[X](x: X): Res[X] = x match {
  case _: Foo => String
  case _: Bar => Int
}

However, if Res[Bar] where to reduce to Int, we would be able to use multiple inheritence to get a class cast exception:

class Boom extends Foo with Bar
val i: Int = res[Boom](new Boom())
println(i + 1) //java.lang.ClassCastException: class String cannot be cast to Integer

If Foo and Bar are classes, the compiler knows that Foo and Bar are disjoint types, in which case it's fine to do the reduction. Likewise, if you swap the order of cases, reducing to Int is also OK given that Bar (the match type's scrutinee) is a subtype of Bar (the match type's first pattern).

@travisbrown
Copy link
Contributor Author

@OlivierBlanvillain Hmm, makes sense, thanks. This means I can't ever expect matching on function types to be useful, right, since FunctionX is a trait? My original use case looked like this:

scala> type Res[F] = F match {
     |   case ((a, b) => t) => String
     |   case ( a     => t) => Int
     | }

scala> 0: Res[Int => Int]
1 |0: Res[Int => Int]
  |^
  |Found:    (0 : Int)
  |Required: Res[Int => Int]

@OlivierBlanvillain
Copy link
Contributor

OlivierBlanvillain commented Mar 11, 2020

Yes, matching on fonction types isn't going to lead to anywhere. In your example I don't think functions being traits is the problem, instead is has to do with variance. Any => Int is a subtype of both Int => Int and ((a, b) => t), so it would be wrong to reduce past the first case in your last example.

I know it's currently really hard to understand why certain match types don't reduce... I plan to invest time into better error messages for match types, in the worse case something à la -Xlog-implicits should be relatively easy to implement.

@travisbrown
Copy link
Contributor Author

@OlivierBlanvillain Just to clarify for the record, I don't need to distinguish Function1[Tuple2[a, b], t] and Function1[a, t], only Function1 and Function2, so I don't think variance is the issue.

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

No branches or pull requests

2 participants