question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Problem with Type of `Tuple.map` (was: type inference regression)

See original GitHub issue

Compiler version

Works fine in 3.2.0, fails in 3.2.1-RC1-bin-20220904-b5fea82-NIGHTLY

Minimized code

import language.implicitConversions

class Inverse[F[_], G[_]]
type MatchSome[X] = X match { case Some[t] => t }
def matchSome[X](x: X): MatchSome[X] = x match { case k: Some[t] => k.get }
given Inverse[MatchSome, Some] = new Inverse

extension [T](x: T) inline def widen[S >: T]: S = x

given [X, Y](using ev: X =:= Y): Conversion[X, Y] = ev(_)
inline def rel[A] = <:<.refl[Any].asInstanceOf[A]
given simplify_map_map[T <: Tuple, F[_], G[_]]: (Tuple.Map[Tuple.Map[T, G], F] =:= Tuple.Map[T, [X] =>> F[G[X]]]) = rel
given simplify_map_id[T <: Tuple]: (Tuple.Map[T, [X] =>> X] =:= T) = rel
given mapinverse_inversemap[T <: Tuple, F[_], G[_]](using Inverse[F, G]): (Tuple.Map[T, F] =:= Tuple.InverseMap[T, G]) = rel
given simplify_map_inversemap[T <: Tuple, F[_]]: (Tuple.InverseMap[Tuple.Map[T, F], F] =:= T) = rel
given map_ismappedby[O <: Tuple, F[_], M <: Tuple.Map[O, F]]: Tuple.IsMappedBy[F][M] = rel


def f[T <: Tuple](t: T): Tuple.Map[T, [X] =>> Option[List[X]]] =
  val tl: Tuple.Map[T, List] = t.widen.map([X] => (x: X) => List(x))
  val tol: Tuple.Map[Tuple.Map[T, List], Option] = tl.widen.map([X] => (x: X) => Option(x))
  tol

def g[T <: Tuple](t: T): T =
  val nt: Tuple.Map[T, [X] =>> X] = t.widen.map[[X] =>> X]([X] => (x: X) => {println(x); x})
  nt

def h[T <: Tuple](to: T)(using Tuple.IsMappedBy[Some][T]): Tuple.InverseMap[T, Some] =
  val t: Tuple.Map[T, MatchSome] = to.widen.map([X] => (x: X) => matchSome(x))
  t

def back_forth[T <: Tuple](t: T): T =
  val ts: Tuple.Map[T, Some] = t.widen.map([X] => (x: X) => Some(x))
  val nt = h(ts)
  nt


@main def example =
  println(f(4, 2))
  println(g(4, 2))
  println(back_forth(4, 2))
  println(h(Some(1), Some(2)))

Output

[error] -- [E007] Type Mismatch Error: /home/adamv/IdeaProjects/Test/src/main/scala/tuple_laws.scala:22:42 
[error] 22 |  val tl: Tuple.Map[T, List] = t.widen.map([X] => (x: X) => List(x))
[error]    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Found:    <root>.this.scala.Tuple.Map[(?1 : T), List]
[error]    |Required: <root>.this.scala.Tuple.Map[T, scala.this.package.List]
[error]    |
[error]    |where:    ?1 is an unknown value of type T
[error]    |          T  is a type in method f with bounds <: <root>.this.scala.Tuple
[error]    |
[error]    |
[error]    |Note: a match type could not be fully reduced:
[error]    |
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[(?1 : T), List]
[error]    |  failed since selector  (?1 : T)
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => List[h] *: LazyRef(Tuple$.this.Map[t, List])
[error]    |
[error]    | longer explanation available when compiling with `-explain`
[error] -- [E007] Type Mismatch Error: /home/adamv/IdeaProjects/Test/src/main/scala/tuple_laws.scala:23:63 
[error] 23 |  val tol: Tuple.Map[Tuple.Map[T, List], Option] = tl.widen.map([X] => (x: X) => Option(x))
[error]    |                                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Found:    <root>.this.scala.Tuple.Map[
[error]    |  (?2 : <root>.this.scala.Tuple.Map[T, scala.this.package.List])
[error]    |, scala.this.Option]
[error]    |Required: <root>.this.scala.Tuple.Map[
[error]    |  <root>.this.scala.Tuple.Map[T, scala.this.package.List]
[error]    |, [A] =>> <root>.this.scala.Option[A]]
[error]    |
[error]    |where:    ?2 is an unknown value of type <root>.this.scala.Tuple.Map[T, scala.this.package.List]
[error]    |          T  is a type in method f with bounds <: <root>.this.scala.Tuple
[error]    |
[error]    |
[error]    |Note: a match type could not be fully reduced:
[error]    |
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[
[error]    |  (?2 : <root>.this.scala.Tuple.Map[T, scala.this.package.List])
[error]    |, scala.this.Option]
[error]    |  failed since selector  T
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => List[h] *: LazyRef(Tuple$.this.Map[t, scala.this.package.List])
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[T, scala.this.package.List]
[error]    |  failed since selector  T
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => List[h] *: LazyRef(Tuple$.this.Map[t, scala.this.package.List])
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[T, scala.this.package.List]
[error]    |  failed since selector  T
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => List[h] *: LazyRef(Tuple$.this.Map[t, scala.this.package.List])
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[T, scala.this.package.List]
[error]    |  failed since selector  T
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => List[h] *: LazyRef(Tuple$.this.Map[t, scala.this.package.List])
[error]    |
[error]    | longer explanation available when compiling with `-explain`
[error] -- [E007] Type Mismatch Error: /home/adamv/IdeaProjects/Test/src/main/scala/tuple_laws.scala:27:58 
[error] 27 |  val nt: Tuple.Map[T, [X] =>> X] = t.widen.map[[X] =>> X]([X] => (x: X) => {println(x); x})
[error]    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Found:    <root>.this.scala.Tuple.Map[(?3 : T), [X] =>> X]
[error]    |Required: <root>.this.scala.Tuple.Map[T, [X] =>> X]
[error]    |
[error]    |where:    ?3 is an unknown value of type T
[error]    |          T  is a type in method g with bounds <: <root>.this.scala.Tuple
[error]    |
[error]    |
[error]    |Note: a match type could not be fully reduced:
[error]    |
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[(?3 : T), [X] =>> X]
[error]    |  failed since selector  (?3 : T)
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => h *: LazyRef(Tuple$.this.Map[t, [X] =>> X])
[error]    |
[error]    | longer explanation available when compiling with `-explain`
[error] -- [E007] Type Mismatch Error: /home/adamv/IdeaProjects/Test/src/main/scala/tuple_laws.scala:31:47 
[error] 31 |  val t: Tuple.Map[T, MatchSome] = to.widen.map([X] => (x: X) => matchSome(x))
[error]    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Found:    <root>.this.scala.Tuple.Map[(?4 : T), 
[error]    |  tuple_laws.this.tuple_laws$package.MatchSome
[error]    |]
[error]    |Required: <root>.this.scala.Tuple.Map[T, tuple_laws.this.tuple_laws$package.MatchSome]
[error]    |
[error]    |where:    ?4 is an unknown value of type T
[error]    |          T  is a type in method h with bounds <: <root>.this.scala.Tuple
[error]    |
[error]    |
[error]    |Note: a match type could not be fully reduced:
[error]    |
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[(?4 : T), 
[error]    |  tuple_laws.this.tuple_laws$package.MatchSome
[error]    |]
[error]    |  failed since selector  (?4 : T)
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => tuple_laws.this.tuple_laws$package.MatchSome[h] *: 
[error]    |  LazyRef(Tuple$.this.Map[t, tuple_laws.this.tuple_laws$package.MatchSome])
[error]    |
[error]    | longer explanation available when compiling with `-explain`
[error] -- [E007] Type Mismatch Error: /home/adamv/IdeaProjects/Test/src/main/scala/tuple_laws.scala:35:42 
[error] 35 |  val ts: Tuple.Map[T, Some] = t.widen.map([X] => (x: X) => Some(x))
[error]    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |Found:    <root>.this.scala.Tuple.Map[(?5 : T), scala.this.Some]
[error]    |Required: <root>.this.scala.Tuple.Map[T, [A] =>> <root>.this.scala.Some[A]]
[error]    |
[error]    |where:    ?5 is an unknown value of type T
[error]    |          T  is a type in method back_forth with bounds <: <root>.this.scala.Tuple
[error]    |
[error]    |
[error]    |Note: a match type could not be fully reduced:
[error]    |
[error]    |  trying to reduce  <root>.this.scala.Tuple.Map[(?5 : T), scala.this.Some]
[error]    |  failed since selector  (?5 : T)
[error]    |  does not match  case <root>.this.scala.Tuple$package.EmptyTuple => <root>.this.scala.Tuple$package.EmptyTuple
[error]    |  and cannot be shown to be disjoint from it either.
[error]    |  Therefore, reduction cannot advance to the remaining case
[error]    |
[error]    |    case h *: t => scala.this.Some[h] *: LazyRef(Tuple$.this.Map[t, scala.this.Some])
[error]    |
[error]    | longer explanation available when compiling with `-explain`

Expectation

Work as defined in 3.2.0.

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
oderskycommented, Sep 12, 2022

After digging deeper it looks like a fundamental problem with Tuple.map to me, which was only hidden by a hole in asSeenFrom. To recap:

t.map([X] => (x: X) => List(x))

is of type

Tuple.Map[t.type, List]

Map is invariant so this is not compatible with Tuple.Map[T, List]. The previous trick of using

(t: T).map([X] => (x: X) => List(x))

(which is essentially what widen does) does not work either. Because the prefix T occurs invariantly in the type of map it has to be narrowed to a skolem. So you get

Tuple.Map[?1: T, List]

The problem seems to be that map does not widen the key type of the map. Indeed it is defined like this:

  /** Called on a tuple `(a1, ..., an)`, returns a new tuple `(f(a1), ..., f(an))`.
   *  The result is typed as `(F[A1], ..., F[An])` if the tuple type is fully known.
   *  If the tuple is of the form `a1 *: ... *: Tuple` (that is, the tail is not known
   *  to be the cons type.
   */
  inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] =
    runtime.Tuples.map(this, f).asInstanceOf[Map[this.type, F]]

So the result is this.type. I believe it should be This instead.

Also the doc comment is ill-formed and incomplete, so that needs to be fixed as well.

0reactions
Kordyjancommented, Dec 5, 2022

@anatoliykmetyuk Is your fix complete? Is there a reason why it wasn’t put into a pr?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Type tuples no longer inferred properly from rest arguments ...
I started using the pattern a few years ago due to having issues with unwanted mapping over non-numeric keys and I believe that...
Read more >
Issue around Mapped Tuple Types with type inference
I'm trying to wrap my head around a problem concerning mapped tuple types. They appear to work when I explicitly define a type...
Read more >
Better type inference for Scala: send us your problematic cases!
This thread is specifically about improving Dotty's type inference. For Scala 2 issues, I suggest opening an issue on github.com/scala/bug/issues.
Read more >
Proposal: Always flatten the single element tuple - Discussion - Swift ...
The problem can't be solved just by not implementing SE-0110, as in Swift4 we should have two separate function types: one that takes...
Read more >
Simple linear regression with ReactiveMP - RuleMethodError?
Stacktrace: [1] error(s::String) @ Base ./error.jl:33 [2] inference(; model::ReactiveMP.ModelGenerator{linreg, Tuple{Int64}, Base.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found