Referential transparency does not operate at the character stream level.
val x = 4
val y = 2
xy does not mean 42
Referential transparency does not operate at the character stream level.
val x = 4
val y = 2
xy does not mean 42
I never mentioned referential transparency. I was only referring to the post that started this discussion. If only a handful of us feel they could be caught by surprise (and furthermore, this handful consists of programmers dumb enough to believe that after a val i='o'
declaration, their program is now using whole
loops), then thereās no problem. We (the handful) might get burn once, and learn our lesson.
The question remains: without thinking too much about it (i.e., while coding, not pondering lexing and parsing), whatās the expected value for most programmers of a
, b
and c
in:
val x = 2
val a = -x.abs
val b = -2.abs
val c = - 2.abs
It looks like M. Oderski already thinks the case of c
calls for a fix. However, after the fix, āare people coolā (to quote the first post) with the fact that b
and c
would be different? Who would prefer all three to be equal (notwithstanding implementation difficulties)?
So I came across this issue writing my own data lexer and parser and being unsure how to deal with prefix operators, decided to check how Scala did it.
I havenāt made a second post until now, because Iām really not sure what the best solution is. One thing on which I do have a firm opinion is that infix operators should require spaces. I see no benefit in allowing
x+y
What would be the benefit in not allowing it?
Tuplet (my soon-to-be-language) allows a+b
as a valid variable name, while a + b
is a.+(b)
(well, reflect.classOf(a).members.find(Ī» ((m)) => m.name == "+" && m.signature == reflect.lambdaSig(nil, List(reflect.classOf(b)), reflect.anyType)).call(List(b))
)
So my first question when approaching syntax is to ask, why does every programming language need its own abstract syntax tree? If every programming language needs its own AST, it suggests to me that they are not really abstract and should be referred to as NASTs. If you go to University in England, whether you study history, sociology, psychology or English, you will use the same grammar and even different Engineering disciplines tend to use the same mathematical notation rather creating their own grammar for every department.
In fact its even worse, because in Scala, even applications like SBT must have their own grammar. So Iām at one extreme. I question the value of special key word syntax, but even if that is a bridge too far and programmers canāt share a common AST, could we not share a common lexer? In that common lexer it seems to me that
- 4
should definitely be 2 tokens. the question is then whether
-4 //and
-4.2
should be treated as single tokens or pairs of tokens. But I definitely think that
- 4.abs
should return -4
.
Whatever the expectations, there isnāt room for debate, here. We must preserve the rules of operator precedence, because applications silently changing their semantics like that when they upgrade the version of Scala is a terrible thing to do.
Therefore, the only correct answer is to do what Scala 2 does:
>scala
Welcome to Scala 2.13.4 (OpenJDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.
scala> -2.abs
val res0: Int = 2
scala> - 2.abs
val res1: Int = 2
scala> ( - 2.abs)
val res2: Int = 2
scala> ( -2.abs)
val res3: Int = 2
scala> val x = 2
val x: Int = 2
scala> -x.abs
val res4: Int = -2
scala> - x.abs
val res5: Int = -2
Please not, -
should be a prefix and an infix operator, thatās it. And .
has higher precedence as infix than -
as prefix.
Of course this would theoretically make sense as it would unify alphanum and op into one thing, but there are two points against this:
Also
scala> import language.postfixOps
import language.postfixOps
scala> - 2 abs
val res0: Int = 2
Well, it has to be a simple expression, so that is clear.
Also
class C:
def f = 42
- 1
where the space rule aligns with leading infix. Without the space, it is not leading infix but unary. That is the same behavior under -Xsource:3
.
For what itās worth, the Java Language Spec has a (rather messy) solution for this issue. From JLS 3.10.1:
The largest decimal literal of type
int
is2147483648
(2^31).All decimal literals from
0
to2147483647
may appear anywhere anint
literal may appear. The decimal literal2147483648
may appear only as the operand of the unary minus operator-
(Ā§15.15.4).It is a compile-time error if the decimal literal
2147483648
appears anywhere other than as the operand of the unary minus operator; or if a decimal literal of typeint
is larger than2147483648
(2^31).
Weird that the JLS doesnāt just admit that -2
is a literal. They donāt even have the issue that this topic brought up, since their ints canāt participate in method selections like 2.abs
.
could we make warning in literal case?
scala> - 2.abs
warning: - 2.abs will be treated as (-2).abs.
To hide this warning wrap your literal in brackets
or use compiler flag -Wdont-warn-neg-litaral
val res1: Int = 2
@sjrd I agree, itās not worth to change things wrt to Scala 2. So weāll leave it as it is.
What about the warning idea? Personally, I would have failed to think of -2 as a literal, but I do pay attention to warnings.
The backport would go under -Xsource:3
, where anything goes.
No one commented nostalgically about -2.abs
parsed as postfix (-2.) abs
or 2.0
.
Here is a fun example in a commit message from ancient days:
-5.+(10) == 5.0 // and not -15.0
I was thinking of a later commit to disambiguate floats that look like selections from ints:
5.f
5.e0
Maybe someone wants to implement that and do a PR? But warnings should be spot-on and not annoying. And enough people see -2.abs
as a literal followed by .abs
that a warning about this would be annoying. So the only real ground for a warning would be - 2.abs
. And there, frankly, I wonder whether anybody would write that who is not actively trying to wring out a puzzler from the compiler. So I doubt that itās worthwhile to add a warning even then.
Donāt laugh but Iām wondering why print-tab says
scala> - -42 //print
-.`unary_-` // : <error>
So what Iād like in REPL is a mode to show me the parse tree or expression tree with values, similar to expecty power asserts.
Someone was just asking about mixing operators such as this artificial example:
scala> 42 $plus 1 == 43
^
error: overloaded method + with alternatives:
(x: Double)Double <and>
(x: Float)Float <and>
(x: Long)Long <and>
(x: Int)Int <and>
(x: Char)Int <and>
(x: Short)Int <and>
(x: Byte)Int <and>
(x: String)String
cannot be applied to (Boolean)
but the OP op was an ordinary method name.