scala / scala-parallel-collections

Parallel collections standard library module for Scala 2.13+

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Nil.par.reduceLeft should throw UnsupportedOperationException, not NoSuchElementException

retronym opened this issue · comments

% scala --scala-version 2.13.3 -Corg.scala-lang.modules::scala-parallel-collections:1.0.0-RC1
Welcome to Scala 2.13.3 (OpenJDK 64-Bit Server VM, Java 14.0.1).
Type in expressions for evaluation. Or try :help.

scala> List.empty[String].reduce(_ + _)
java.lang.UnsupportedOperationException: empty.reduceLeft
  at scala.collection.IterableOnceOps.reduceLeft(IterableOnce.scala:723)
  at scala.collection.IterableOnceOps.reduceLeft$(IterableOnce.scala:720)
  at scala.collection.AbstractIterable.reduceLeft(Iterable.scala:920)
  at scala.collection.IterableOnceOps.reduce(IterableOnce.scala:692)
  at scala.collection.IterableOnceOps.reduce$(IterableOnce.scala:692)
  at scala.collection.AbstractIterable.reduce(Iterable.scala:920)
  ... 32 elided

scala> import scala.collection.parallel.CollectionConverters._
import scala.collection.parallel.CollectionConverters._

scala> List.empty[String].par.reduce(_ + _)
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:627)
  at scala.None$.get(Option.scala:626)
  at scala.collection.parallel.ParIterableLike.reduce(ParIterableLike.scala:361)
  at scala.collection.parallel.ParIterableLike.reduce$(ParIterableLike.scala:360)
  at scala.collection.parallel.immutable.ParVector.reduce(ParVector.scala:40)
  ... 32 elided

Nil.par.min and Nil.par.max may have the same issue.

I have used "org.scala-lang.modules" %% "scala-parallel-collections" % "1.0.1".

root@9d686781776b:/opt/scala-parallel-collections/sandbox# sbt
[info] welcome to sbt 1.4.3 (AdoptOpenJDK Java 11.0.9.1)
[info] loading project definition from /opt/scala-parallel-collections/sandbox/project
[info] loading settings for project sandbox from build.sbt ...
[info] set current project to sandbox (in build file:/opt/scala-parallel-collections/sandbox/)
[info] sbt server started at local:///root/.sbt/1.0/server/ac048f6b25b988f0e872/sock
[info] started sbt server
sbt:sandbox> console
[info] Starting scala interpreter...
Welcome to Scala 2.13.5 (OpenJDK 64-Bit Server VM, Java 11.0.9.1).
Type in expressions for evaluation. Or try :help.

scala> import scala.collection.parallel.CollectionConverters._
import scala.collection.parallel.CollectionConverters._

scala> List.empty[Int].min
java.lang.UnsupportedOperationException: empty.min
  at scala.collection.IterableOnceOps.min(IterableOnce.scala:914)
  at scala.collection.IterableOnceOps.min$(IterableOnce.scala:912)
  at scala.collection.AbstractIterable.min(Iterable.scala:919)
  ... 35 elided

scala> List.empty[Int].par.min
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:627)
  at scala.None$.get(Option.scala:626)
  at scala.collection.parallel.ParIterableLike.min(ParIterableLike.scala:466)
  at scala.collection.parallel.ParIterableLike.min$(ParIterableLike.scala:465)
  at scala.collection.parallel.immutable.ParVector.min(ParVector.scala:40)
  ... 35 elided

scala> List.empty[Int].max
java.lang.UnsupportedOperationException: empty.max
  at scala.collection.IterableOnceOps.max(IterableOnce.scala:945)
  at scala.collection.IterableOnceOps.max$(IterableOnce.scala:943)
  at scala.collection.AbstractIterable.max(Iterable.scala:919)
  ... 35 elided

scala> List.empty[Int].par.max
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:627)
  at scala.None$.get(Option.scala:626)
  at scala.collection.parallel.ParIterableLike.max(ParIterableLike.scala:470)
  at scala.collection.parallel.ParIterableLike.max$(ParIterableLike.scala:469)
  at scala.collection.parallel.immutable.ParVector.max(ParVector.scala:40)
  ... 35 elided

But, Nil.par.minBy and Nil.par.maxBy don't have.

scala> List.empty[String].minBy(_.length)
java.lang.UnsupportedOperationException: empty.minBy
  at scala.collection.IterableOnceOps.minBy(IterableOnce.scala:1025)
  at scala.collection.IterableOnceOps.minBy$(IterableOnce.scala:1023)
  at scala.collection.AbstractIterable.minBy(Iterable.scala:919)
  ... 35 elided

scala> List.empty[String].par.minBy(_.length)
java.lang.UnsupportedOperationException: empty.minBy
  at scala.collection.parallel.ParIterableLike.minBy(ParIterableLike.scala:480)
  at scala.collection.parallel.ParIterableLike.minBy$(ParIterableLike.scala:479)
  at scala.collection.parallel.immutable.ParVector.minBy(ParVector.scala:40)
  ... 35 elided

scala> List.empty[String].maxBy(_.length)
java.lang.UnsupportedOperationException: empty.maxBy
  at scala.collection.IterableOnceOps.maxBy(IterableOnce.scala:978)
  at scala.collection.IterableOnceOps.maxBy$(IterableOnce.scala:976)
  at scala.collection.AbstractIterable.maxBy(Iterable.scala:919)
  ... 35 elided

scala> List.empty[String].par.maxBy(_.length)
java.lang.UnsupportedOperationException: empty.maxBy
  at scala.collection.parallel.ParIterableLike.maxBy(ParIterableLike.scala:474)
  at scala.collection.parallel.ParIterableLike.maxBy$(ParIterableLike.scala:473)
  at scala.collection.parallel.immutable.ParVector.maxBy(ParVector.scala:40)
  ... 35 elided

maxBy throws UnsupportedOperationException if a collection is empty 😄 .

https://github.com/scala/scala-parallel-collections/blob/v1.0.1/core/src/main/scala/scala/collection/parallel/ParIterableLike.scala#L473-L477

  def maxBy[S](f: T => S)(implicit cmp: Ordering[S]): T = {
    if (isEmpty) throw new UnsupportedOperationException("empty.maxBy")

    reduce((x, y) => if (cmp.gteq(f(x), f(y))) x else y)
  }

reduce does not 😢 .
tasksupport.executeAndWaitResult(new Reduce(op, splitter)) produces None if a collection is empty.
Then, None.get throws NoSuchElementException.

https://github.com/scala/scala-parallel-collections/blob/v1.0.1/core/src/main/scala/scala/collection/parallel/ParIterableLike.scala#L360-L362

  def reduce[U >: T](op: (U, U) => U): U = {
    tasksupport.executeAndWaitResult(new Reduce(op, splitter)).get
  }

IMO, it would be nice to check whether a collection is empty or not at first in the reduce function.

FYI, scala.collection.IterableOnce also does.

https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/IterableOnce.scala#L724-L741

  def reduceLeft[B >: A](op: (B, A) => B): B = {
    val it = iterator
    if (it.isEmpty)
      throw new UnsupportedOperationException("empty.reduceLeft")
    //...

I attempt to resolve this issue at #167 😃