scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.79k stars 1.04k forks source link

Expressiveness shortcoming when composing functions over capturing data #16268

Open odersky opened 1 year ago

odersky commented 1 year ago

Compiler version

3.2.2-RC1

Minimized example

import language.experimental.captureChecking
class Tree
case class Thicket(trees: List[Tree]) extends Tree

def test1(segments: List[{*} Tree]) =
  val elems = segments flatMap { (t: {*} Tree) => t match // error
    case ts: Thicket => ts.trees.tail
    case t => Nil
  }
  elems

Output

5 |  val elems = segments flatMap { (t: {*} Tree) => t match // error
  |                               ^
  |                      Found:    ? ({*} Tree) -> ? IterableOnce[? Tree]
  |                      Required: (box {*} Tree) => IterableOnce[box ? Tree]
6 |    case ts: Thicket => ts.trees.tail
7 |    case t => Nil
8 |  }

Expectation

Not sure. The problem is that box adaptation refuses to work since the box is over the universal set {*}. To compare, the following program compiles OK:

def test3(c: {*} Any)(segments: List[{c} Tree]) =
  val elems = segments flatMap { (t: {c} Tree) => t match
    case ts: Thicket => ts.trees.tail
    case t => Nil
  }
  elems

Here box adaptation works since the required type is

(box {c} Tree) => IterableOnce[box ? Tree]

It seems reasonable that something like this should also work for {*}. If not, we seem to have a shortcoming in applying higher-order functions to data with universal capabilities. But I don't know at present what should be tweaked. Is it

Gedochao commented 7 months ago

Is this syntax (and as an extension, this ticket) still valid? cc @odersky @Linyxus

Trying to replicate with the minimised example on main:

import language.experimental.captureChecking
class Tree
case class Thicket(trees: List[Tree]) extends Tree

def test1(segments: List[{*} Tree]) =
  val elems = segments flatMap { (t: {*} Tree) => t match // error
    case ts: Thicket => ts.trees.tail
    case t => Nil
  }
  elems

gives me an error, yes, but it's a syntax one:

-- [E103] Syntax Error: smth.scala:5:26 ----------------------------------------
5 |def test1(segments: List[{*} Tree]) =
  |                          ^
  |                          Illegal start of declaration
  |
  | longer explanation available when compiling with `-explain`
-- [E040] Syntax Error: smth.scala:5:29 ----------------------------------------
5 |def test1(segments: List[{*} Tree]) =
  |                             ^^^^
  |                             ',' or ']' expected, but identifier found
-- [E103] Syntax Error: smth.scala:6:38 ----------------------------------------
6 |  val elems = segments flatMap { (t: {*} Tree) => t match // error
  |                                      ^
  |                                      Illegal start of declaration
  |
  | longer explanation available when compiling with `-explain`
-- [E040] Syntax Error: smth.scala:6:41 ----------------------------------------
6 |  val elems = segments flatMap { (t: {*} Tree) => t match // error
  |                                         ^^^^
  |                                 ',' or ')' expected, but identifier found
4 errors found

Similarly, the original post implies the other example should compile, while it currently does not:

import language.experimental.captureChecking
class Tree
case class Thicket(trees: List[Tree]) extends Tree

def test3(c: {*} Any)(segments: List[{c} Tree]) =
  val elems = segments flatMap { (t: {c} Tree) => t match
    case ts: Thicket => ts.trees.tail
    case t => Nil
  }
  elems

This gives me:

-- [E103] Syntax Error: smth.scala:5:14 ----------------------------------------
5 |def test3(c: {*} Any)(segments: List[{c} Tree]) =
  |              ^
  |              Illegal start of declaration
  |
  | longer explanation available when compiling with `-explain`
-- [E040] Syntax Error: smth.scala:5:17 ----------------------------------------
5 |def test3(c: {*} Any)(segments: List[{c} Tree]) =
  |                 ^^^
  |                 ',' or ')' expected, but identifier found
-- [E103] Syntax Error: smth.scala:5:38 ----------------------------------------
5 |def test3(c: {*} Any)(segments: List[{c} Tree]) =
  |                                      ^
  |                                      Illegal start of declaration
  |
  | longer explanation available when compiling with `-explain`
-- [E040] Syntax Error: smth.scala:5:41 ----------------------------------------
5 |def test3(c: {*} Any)(segments: List[{c} Tree]) =
  |                                         ^^^^
  |                                 ',' or ']' expected, but identifier found
-- [E103] Syntax Error: smth.scala:6:38 ----------------------------------------
6 |  val elems = segments flatMap { (t: {c} Tree) => t match
  |                                      ^
  |                                      Illegal start of declaration
  |
  | longer explanation available when compiling with `-explain`
-- [E040] Syntax Error: smth.scala:6:41 ----------------------------------------
6 |  val elems = segments flatMap { (t: {c} Tree) => t match
  |                                         ^^^^
  |                                 ',' or ')' expected, but identifier found
6 errors found