Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does flatten on nested Iterator not compile and why do I need type ascription?

(new Iterator[List[Int]] {
  def hasNext: Boolean = ???
  def next(): List[Int] = ???
}).flatten

gives error:

value flatten is not a member of Iterator[List[Int]]
[error] possible cause: maybe a semicolon is missing before `value flatten'?
[error]     }.flatten
[error]       ^
[error] one error found

But

(new Iterator[List[Int]] {
  def hasNext: Boolean = ???
  def next(): List[Int] = ???
}: Iterator[List[Int]]).flatten

works. Also storing the iterator in a val works.

Scala version: 2.11.8

like image 490
erprincipe Avatar asked Jun 02 '20 13:06

erprincipe


2 Answers

I believe this issue was addressed by Include the parts of a compound/refinement type in implicit scope. #5867. The problem was companion to anonymous classes used to not qualify for implicit search

I think aaf9198#diff-7c03397456d3b987549fd039d6b639c6R516 was the first to exclude refinement/anon classes from contributing to companion implicits. @odersky Can you remember the motivation?

Here is a minimal reproduction

trait A {
  def foo(): Int
}
class B {
  def bar(): String = "woohoo"
}
object A {
  implicit def aToB(a: A): B = new B
}

(new A {
  override def foo(): Int = 42
}).bar()

// Error: 
// value bar is not a member of A$A12.this.A 
// possible cause: maybe a semicolon is missing before `value bar'? }).bar();

Therefore since in 2.11 and 2.12 flatten on Iterator was provided as an extension method via flattenTraversableOnce the issue would manifest for anonymous Iterator class.


The following is my answer prior to the edit above:

It seems to have something to do with implicit resolution in both 2.11 and 2.12. If you explicitly import flatten as an extension method via

import scala.collection.TraversableOnce.flattenTraversableOnce

then it seems to work. The issue seems to be fixed since 2.13.0-M3 where typer phase gives

collection.this.TraversableOnce.flattenTraversableOnce[Int, List]({
  final class $anon extends AnyRef with Iterator[List[Int]] {
    def <init>(): <$anon: Iterator[List[Int]]> = {
      $anon.super.<init>();
      ()
    };
    def hasNext: Boolean = scala.Predef.???;
    def next(): List[Int] = scala.Predef.???
  };
  new $anon()
})(scala.Predef.$conforms[List[Int]]).flatten

whilst in 2.13.0 release flatten no longer seem to be provided via extension method

{
  final class $anon extends AnyRef with Iterator[List[Int]] {
    def <init>(): <$anon: Iterator[List[Int]]> = {
      $anon.super.<init>();
      ()
    };
    def hasNext: Boolean = scala.Predef.???;
    def next(): List[Int] = scala.Predef.???
  };
  new $anon()
}.flatten[Int](scala.Predef.$conforms[List[Int]])

The above expansion seems to be explained by SLS 6.4 Designators

𝑒.𝑥 is typed as if it was { val 𝑦 = 𝑒; 𝑦.𝑥 }

like image 120
Mario Galic Avatar answered Oct 19 '22 22:10

Mario Galic


I think it tries to parse this:

new Iterator[List[Int]] {
      def hasNext: Boolean = ???
      def next(): List[Int] = ???
    }.flatten

as this

new Iterator[List[Int]] ( {
      def hasNext: Boolean = ???
      def next(): List[Int] = ???
    }.flatten )

Notice where I added the parentheses ().

like image 1
Saša Zejnilović Avatar answered Oct 19 '22 21:10

Saša Zejnilović