Scala Programming Language
  1. Scala Programming Language
  2. SI-4843

Abstracting over sequential and parallel sequences broken

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Critical Critical
    • Resolution: Fixed
    • Affects Version/s: Scala 2.9.0, Scala 2.9.1
    • Fix Version/s: None
    • Component/s: Misc Library
    • Labels:
      None
    • Environment:

      Scala 2.9.0.1 and 2.9.1.RC1 on OS X Lion (Mac)

      Description

      The below REPL examples shows the observed behavior under Scala 2.9.0.1 and 2.9.1.RC1. Under 2.9.0.1 there seem to be two bugs, one regarding type inference and the other regarding abstracting over sequential and parallel Seqs (that might also apply to other collections). Under 2.9.1.RC1 only the later occurs, yet the type inferencer seems to use a structural type instead of GenSeq.

      The real issue from a user's perspective is, that even if a parallel Seq is returned by the numbers-method, map won't be executed in parallel, i.e. abstracting over sequential and parallel collections is broken.

      ===============================================================================
      Scala 2.9.0.1
      ===============================================================================

      Welcome to Scala version 2.9.0.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
      Type in expressions to have them evaluated.
      Type :help for more information.

      scala> def numbers(isPar: Boolean) =

      if (isPar) (1 to 8).par else 1 to 8
      <console>:7: error: kinds of the type arguments (scala.collection.GenSeq[Any] {def seq: scala.collection.immutable.Seq[Any]}

      ) do not conform to the expected kinds of the type parameters (type CC) in class GenericCompanion.
      scala.collection.GenSeq[Any]

      {def seq: scala.collection.immutable.Seq[Any]}

      's type parameters do not match type CC's expected parameters: <none> has no type parameters, but type CC has one
      def numbers(isPar: Boolean) =
      ^

      scala> def numbers(isPar: Boolean): scala.collection.GenSeq[Int] =

      if (isPar) (1 to 8).par else 1 to 8
      numbers: (isPar: Boolean)scala.collection.GenSeq[Int]

      scala> numbers(true) map

      { _ => Thread.currentThread.getName }
      res0: scala.collection.GenSeq[java.lang.String] = ParVector(Thread-5, Thread-5, Thread-5, Thread-5, Thread-5, Thread-5, Thread-5, Thread-5)

      ===============================================================================
      Scala 2.9.1.RC1
      ===============================================================================

      Welcome to Scala version 2.9.1.RC1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_26).
      Type in expressions to have them evaluated.
      Type :help for more information.

      scala> def numbers(isPar: Boolean) =
      | if (isPar) (1 to 8).par else 1 to 8
      numbers: (isPar: Boolean)scala.collection.GenSeq[Int] with Serializable{def companion: scala.collection.generic.GenericCompanion[scala.collection.GenSeq{def seq: scala.collection.immutable.Seq[Any]}]; def par: scala.collection.parallel.immutable.ParSeq[Int]; def seq: scala.collection.immutable.Seq[Int]; def distinct: scala.collection.GenSeq[Int]{def seq: scala.collection.immutable.Seq[Int]; def distinct: scala.collection.GenSeq[Int]{def seq: scala.collection.immutable.Seq[Int]; protected def parCombiner: scala.collection.parallel.Combiner[Int,scala.collection.parallel.immutable.ParSeq[Int]]}; def intersect[U >: Int](that: scala.collection.GenSeq[U]): scala.collection.GenSeq[Int]{def seq: scala.collection.immutable.Seq[Int]; protected def parCombiner: scala.collection.parallel.Combiner[I...
      scala> numbers(true) map { _ => Thread.currentThread.getName }

      res0: scala.collection.GenSeq[java.lang.String] = ParVector(Thread-5, Thread-5, Thread-5, Thread-5, Thread-5, Thread-5, Thread-5, Thread-5)

      scala> def numbers(isPar: Boolean): scala.collection.GenSeq[Int] =

      if (isPar) (1 to 8).par else 1 to 8
      numbers: (isPar: Boolean)scala.collection.GenSeq[Int]

      scala> numbers(true) map

      { _ => Thread.currentThread.getName }

      res1: scala.collection.GenSeq[java.lang.String] = ParVector(Thread-7, Thread-7, Thread-7, Thread-7, Thread-7, Thread-7, Thread-7, Thread-7)

        Activity

        Hide
        Heiko Seeberger added a comment -

        This looks fixed in 2.10 (scala-2.10.0.r25517-b20110816023619), i.e. could probably be closed.
        Thanks!

        Show
        Heiko Seeberger added a comment - This looks fixed in 2.10 (scala-2.10.0.r25517-b20110816023619), i.e. could probably be closed. Thanks!
        Hide
        Johannes Rudolph added a comment -

        Just a note for someone who stumbles on this later on: This is fixed for Scala >= 2.10 but isn't and won't be fixed for Scala 2.9.x because it can't be fixed without breaking binary compatibility.

        Show
        Johannes Rudolph added a comment - Just a note for someone who stumbles on this later on: This is fixed for Scala >= 2.10 but isn't and won't be fixed for Scala 2.9.x because it can't be fixed without breaking binary compatibility.
        Hide
        Omid Aladini added a comment -

        Could you please shed some light on why this was happening on 2.9.x? Any pointers to discussions about this or the fix on 2.10?

        Show
        Omid Aladini added a comment - Could you please shed some light on why this was happening on 2.9.x? Any pointers to discussions about this or the fix on 2.10?
        Hide
        Johannes Rudolph added a comment -

        In 2.9.x the behavior depends on the right implicit CanBuildFrom being chosen. But the decision which CanBuildFrom to take is based on the static type of the collection, so that when you had GenSeq the parallel CanBuildFrom was never chosen.

        In 2.10 the behavior changed to not rely on static type information.

        I don't know the exact commit of the fix but here's a former discussion on the mailing list from the time the issue was fixed:

        https://groups.google.com/d/topic/scala-language/5KZAZXnWS1I/discussion

        Show
        Johannes Rudolph added a comment - In 2.9.x the behavior depends on the right implicit CanBuildFrom being chosen. But the decision which CanBuildFrom to take is based on the static type of the collection, so that when you had GenSeq the parallel CanBuildFrom was never chosen. In 2.10 the behavior changed to not rely on static type information. I don't know the exact commit of the fix but here's a former discussion on the mailing list from the time the issue was fixed: https://groups.google.com/d/topic/scala-language/5KZAZXnWS1I/discussion
        Hide
        Omid Aladini added a comment -

        @jrudolph thanks!

        Show
        Omid Aladini added a comment - @jrudolph thanks!

          People

          • Assignee:
            Unassigned
            Reporter:
            Heiko Seeberger
          • Votes:
            0 Vote for this issue
            Watchers:
            6 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development