Functional Syntax?

Referential transparency does not operate at the character stream level.

val x = 4
val y = 2
xy does not mean 42

5 Likes

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
2 Likes

What would be the benefit in not allowing it?

1 Like

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
9 Likes

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:

  • People forget to issue a space between alphanum and op causing a semantic change silently
  • Itā€™s an evolutionary break in what a user would expect, sometimes this is good, but here it would add a massive dissonance in the data science community expecting to leave out spaces is the same.

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 is 2147483648 (2^31).

All decimal literals from 0 to 2147483647 may appear anywhere an int literal may appear. The decimal literal 2147483648 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 type int is larger than 2147483648 (2^31).

5 Likes

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.

2 Likes

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
11 Likes

@sjrd I agree, itā€™s not worth to change things wrt to Scala 2. So weā€™ll leave it as it is.

1 Like

What about the warning idea? Personally, I would have failed to think of -2 as a literal, but I do pay attention to warnings.

3 Likes

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
1 Like

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.

3 Likes

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.

2 Likes