Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
scabug opened this issue Jul 5, 2011 · 3 comments
Closed

Comments

@scabug
Copy link

scabug commented Jul 5, 2011

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.

@scabug
Copy link
Author

scabug commented Jul 5, 2011

Imported From: https://issues.scala-lang.org/browse/SI-4773?orig=1
Reporter: @pchiusano

@scabug
Copy link
Author

scabug commented Jul 6, 2011

@paulp said:
Although it focuses on the by-name aspect and not inference, this is a duplicate of venerable #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.

@scabug scabug closed this as completed Jul 6, 2011
@scabug
Copy link
Author

scabug commented Jul 6, 2011

@pchiusano said:
Ahhh, you're right. Sorry for the dup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant