Skip to content

Commit 4fae6f7

Browse files
szeigerjvican
authored andcommitted
SIP-NN: Right-Associative By-Name Operators (#805)
1 parent 9b19045 commit 4fae6f7

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
---
2+
layout: sip
3+
discourse: true
4+
title: SIP-NN - Right-Associative By-Name Operators
5+
---
6+
7+
**By: Stefan Zeiger**
8+
9+
## History
10+
11+
| Date | Version |
12+
|---------------|---------------|
13+
| Jul 12th 2017 | Initial Draft |
14+
15+
## Motivation
16+
17+
Scala allows the definition of right-associative by-name operators but the desugaring, as
18+
currently defined, forces the arguments, thus effectively making them by-value. This has
19+
been recognized as a [bug](https://github.com/scala/bug/issues/1980) since 2009.
20+
21+
## Motivating Examples
22+
23+
Apart from the examples mentioned in and linked to [scala/bug#1980](https://github.com/scala/bug/issues/1980),
24+
this has recently come up as a [problem for the collections library redesign](https://github.com/scala/collection-strawman/issues/127)
25+
for Scala 2.13.
26+
27+
Scala 2.12 has a `Stream` type with a lazy tail and a strict head element. Thanks to a clever
28+
[hack](https://github.com/scala/scala/blob/9ab72a204ff3070ffabc3c06f3d381999da43fcd/src/library/scala/collection/immutable/Stream.scala#L1115-L1133)
29+
right-associative by-name operators can work well enough for `Stream`:
30+
31+
scala> def f(i: Int) = { println("Generating "+i); i }
32+
f: (i: Int)Int
33+
34+
scala> f(1) #:: f(2) #:: f(3) #:: Stream.empty
35+
Generating 1
36+
res0: scala.collection.immutable.Stream[Int] = Stream(1, ?)
37+
38+
The `LazyList` type proposed for the new collections library is supposed to be lazy in the head and tail.
39+
This cannot be supported with the existing hack (which always forces the first element in the chain), so we need
40+
a proper fix at the language level.
41+
42+
## Design
43+
44+
The desugaring of binary operators is currently defined in the spec as:
45+
46+
> A left-associative binary
47+
> operation `e1 op e2` is interpreted as `e1.op(e2)`. If `op` is
48+
> right-associative, the same operation is interpreted as
49+
> `{ val x=e1; e2.op(x) }`, where `x` is a fresh name.
50+
51+
It should be changed to:
52+
53+
> A left-associative binary
54+
> operation `e1 op e2` is interpreted as `e1.op(e2)`. If `op` is
55+
> right-associative and its parameter is passed by name, the same operation is interpreted as
56+
> `e2.op(e1)`. If `op` is right-associative and its parameter is passed by value,
57+
> it is interpreted as `{ val x=e1; e2.op(x) }`, where `x` is a fresh name.
58+
59+
This means that all by-value parameters are still forced from left to right but by-name
60+
parameters are not forced anymore. They now behave the same way in operator syntax as they
61+
would when using standard method call syntax.
62+
63+
## Implementation
64+
65+
A complete implementation for Scala 2.13 is provided in [scala/scala#5969](https://github.com/scala/scala/pull/5969).
66+
67+
## Counter-Examples
68+
69+
No change of type inference semantics is implied by the new desugaring. In particular, all parameters to
70+
right-associative operators are still type-checked without an expected type in the current implementation.
71+
72+
It may be desirable to use an expected type, like for a method call, but that is orthogonal to this proposal
73+
and would necessarily apply equally to by-name and by-value parameters. In the case of overloaded
74+
operators it cannot be determined whether the parameter is by-name or by-value without type-checking the
75+
argument first.
76+
77+
## Drawbacks
78+
79+
- This constitutes a silent change in semantics for existing code. Since the current semantics are essentially
80+
broken the likelihood of affecting existing code negatively are low.
81+
82+
- Macros and tooling that works at the Scala AST level may need to be adapted to the new desugaring. This is also
83+
unlikely because the new desugaring produces currently legal Scala code that could have been manually written in
84+
the same way.
85+
86+
## Alternatives
87+
88+
As mentioned above, the current `Stream`
89+
[hack](https://github.com/scala/scala/blob/9ab72a204ff3070ffabc3c06f3d381999da43fcd/src/library/scala/collection/immutable/Stream.scala#L1115-L1133)
90+
can work around this problem in some cases but not all.

0 commit comments

Comments
 (0)