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

Type inference does not flow right to left for right-associative operators

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Duplicate
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Misc Compiler
    • Labels:
      None

      Description

      This might be classified as an enhancement. But here is an example where I would expect type inference to work - I would expect type information to flow from right to left for a right associative operator, but it does not:

      scala>   case class Foo[A]() { def :::(f: Foo[A]) = f }
      defined class Foo
      
      scala>   Foo() ::: Foo[Int]()
      <console>:10: error: type mismatch;
       found   : Foo[Nothing]
       required: Foo[Int]
      Note: Nothing <: Int, but class Foo is invariant in type A.
      You may wish to define A as +A instead. (SLS 4.5)
               Foo() ::: Foo[Int]()
                     ^
      
      scala>   Foo[Int]().:::(Foo())
      res1: Foo[Int] = Foo()
      

      So, explicitly calling the operator results in proper type inference flow, but using the same operator in a right associative way doesn't seem to work. Incidentally, I could fix things in this case by making Foo covariant, but that isn't always possible/desireable.

      I thought this might be a bug, and went looking through the spec to see what the spec'd behavior is. Page 85 of the spec states that right associative operators are desugared to the declaration of a fresh variable, followed by application:

      A left-associative binary operation e1 op e2 is interpreted as e1.op(e2). 
      If op is right-associative, the same operation is interpreted as 
      { val x=e1; e2.op(x ) }, where x is a fresh name.
      

      So I suppose this is behaving exactly as expected. Incidentally, a consequence of this is that right associative operators cannot be usefully non-strict:

      scala> case class Foo[A]() { def :::(f: => Foo[A]) = this }
      defined class Foo
      
      scala> sys.error("todo") :: Foo[Int]()
      <console>:10: error: value :: is not a member of Foo[Int]
             error("todo") :: Foo[Int]()
                           ^
      
      scala> sys.error("todo") ::: Foo[Int]()
      
      java.lang.RuntimeException: todo
              at scala.sys.package$.error(package.scala:27)
              ...
      

      But I have to ask - why are right associative operators desugared this way? It does not seem correct. Since the val is a fresh name, it is not even observable and no code can distinguish it from desugaring a :: b to b.::(a). This will have the benefit of better inference, and it would, incidentally, allow a to be non-strict (which is quite useful in some cases, e.g. a fully lazy stream cons.

      The only other reason I could think for the current behavior is to avoid stack overflows in the case of long chains of right associative operators (which would be extremely rare - they would have to be literal expressions in your program of many hundreds of elements to SO). But I think the proper behavior in this case is to keep the more straightforward desugaring and let the user worry about handling that - perhaps by making the operator non-strict, or by manually chunking.

        Activity

        Hide
        Paul Phillips added a comment -

        Although it focuses on the by-name aspect and not inference, this is a duplicate of venerable SI-1980, where the same remedy you have just proposed is proposed. I'll see if I can reawaken it. I'm not aware of any major resistance to that change, but we have so few people to implement things.

        Show
        Paul Phillips added a comment - Although it focuses on the by-name aspect and not inference, this is a duplicate of venerable SI-1980 , where the same remedy you have just proposed is proposed. I'll see if I can reawaken it. I'm not aware of any major resistance to that change, but we have so few people to implement things.
        Hide
        Paul Chiusano added a comment -

        Ahhh, you're right. Sorry for the dup.

        Show
        Paul Chiusano added a comment - Ahhh, you're right. Sorry for the dup.

          People

          • Assignee:
            Unassigned
            Reporter:
            Paul Chiusano
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development