You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
The text was updated successfully, but these errors were encountered:
@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.
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:
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:
So I suppose this is behaving exactly as expected. Incidentally, a consequence of this is that right associative operators cannot be usefully non-strict:
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.
The text was updated successfully, but these errors were encountered: