Skip to content

Scala 2 regression: Overloads trigger erroneous lambda auto-untupling when applying implicit conversion to a lambda #16108

Closed
@neko-kai

Description

@neko-kai

Compiler version

3.2.1-RC2, 3.2.0, 3.1.3

Minimized code

https://scastie.scala-lang.org/WspTUC2IRQehfdFeA5EvMg

package example

import scala.language.implicitConversions

final class Functoid[+R](val function: Product => R)

object Functoid {
  implicit def apply[A, R](function: A => R): Functoid[R] = {
    println(s"arity 1")
    new Functoid({ case Tuple1(a: A) => function(a) })
  }
  implicit def apply[A, B, R](function: (A, B) => R): Functoid[R] = {
    println("arity 2")
    new Functoid({ case (a: A, b: B) => function(a, b) })
  }
}

final case class ContainerConfig(image: String, version: Int, cmd: String)

final class ContainerResource

object ContainerResource {
  implicit final class DockerProviderExtensions(private val self: Functoid[ContainerResource]) extends AnyVal {
    def modifyConfig(modify: Functoid[ContainerConfig => ContainerConfig]): Functoid[ContainerConfig => ContainerConfig] = modify
    // removing this overload fixes the implicit conversion and returns `arity 2` print
    def modifyConfig(modify: ContainerConfig => ContainerConfig): Functoid[ContainerConfig => ContainerConfig] = new Functoid(_ => modify)
  }
}

object App extends App {
  println {
    new Functoid(_ => new ContainerResource)
      .modifyConfig {
        // applying Functoid.apply explicitly instead of via implicit conversion also avoids untupling
//        Functoid {
        (image: String, version: Int) => (cfg: ContainerConfig) => cfg.copy(image, version)
//        }
      }
      .function.apply(Tuple2("img", 9))
      .apply(ContainerConfig("a", 0, "b"))
  }
}

Output

arity 1
Exception in thread "main" scala.MatchError: (img,9) (of class scala.Tuple2)
	at example.Functoid$.apply$$anonfun$1(example.scala:10)
	at example.example$package$.example(example.scala:38)
	at example.example.main(example.scala:30)

Expectation

Expected to print

arity 2
ContainerConfig(img,9,b)

as on Scala 2 (https://scastie.scala-lang.org/bH7AUwrhS5qQnotQPPi30Q)

The existence of def modifyConfig(modify: ContainerConfig => ContainerConfig) overload causes a very weird implicit conversion of the function, instead of hitting the 2 arity Functoid.apply conversion, the function is auto-untupled and turned into a 1-arity function before hitting the 1-arity Functoid.apply. Functoid.apply being an overload itself has no effect, the issue still manifests if the conversions are called apply1 & apply2.

Removing the overload or applying Functoid.apply explicitly works around the issue.

This code is a minimized version of real library code in https://github.com/izumi/izumi which we're trying to port to Scala 3

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions