3

I recently fell in love with Factor , a programming language in the Forth family that enforces use of whitespace around identifiers, this allows creation of "functions" with extremely terse yet readable names, like [1..b] (it generates a range of integers from 1 to argument (inclusive), there is of course also [1..b)) .

Of (non esoteric) programming languages I think only Forths do this. Why?

One reason that comes to mind is parsing of arithmetic, but adding spaces around operators seems like a fairly common practice, and is even enforced by some language auto-formatters.

Another reason would be a potentially awkward function invocation, like f ( a , b ), but some languages require neither parentheses nor commas for function invocation, like the ML family of languages.

UPDATE 2023-12-11:

Expanding character set in infix operators is a separate concern, and is not the focus of this question.

Rather it is something along the lines of "why is there little experimentation in the function call convention?". (I appreciate that the question is not well formed, but nevertheless I think it is an interesting question for programming language design)

[1..b] x and [a..b] x y and (a..b) x y could be enabled by enforcing whitespace around identifiers, which would be a calling convention not too different from OCaml's range ~stop:`inclusive 1 x and range ~stop:`inclusive x y and range ~start:`exclusive x y .

While I think that functions to generate numeric ranges are an interesting example, since they allow implementing existing mathematical convention in a very readable manner, they are also not the point, so please do not overly focus on these specific examples.

6
  • 2
    The C (and by extension Fortran/Algol) language family has been extremely successful. Every new language that wants to have a chance at popularity will be somewhat similar in order to aid learning and to piggy-back on existing tooling. This means infix operators, no whitespace sensitivity (except maybe indentation as in Python or newline as statement end), and C-style identifier syntax corresponding to a regex for ASCII [a-zA-Z_][a-zA-Z_0-9]*. Differing syntax conventions as in Lisp or Forth are extremely niche. Even kebab-case (as in Perl6, CSS, XML) is super rare.
    – amon
    Dec 10, 2023 at 23:12
  • 5
    "Programming tools" is a market like any other, and a market has to go by what people want. And it so happens that people hate having to type characters that look unnecessary just to satisfy the computer - quite rightfully, IMO. Dec 11, 2023 at 7:49
  • @Blin: There are plenty of experiments in function call syntax. You mention Forth/Factor yourself, there is also Lisp, Smalltalk, ML - all have quite different function call syntax. Haskell supports users-defined operators and have special syntax to turn functions into infix operators and vice versa. And languages with macros (like Lisp or Rust) allow you to define basically any function-call syntax you like. It is just that the most popular languages by far stick with the traditional f(x) syntax.
    – JacquesB
    Dec 12, 2023 at 22:00
  • @JacquesB , Lisp first appeared in 1958, Forth in 1970, Smalltalk in 1972, ML in 1973. Exhausting the design space of calling conventions in the first couple of decades of programming as a discipline sounds unlikely. I went through the languages "first introduced" after 2010 (wikidata query), and only one has a calling convention different from Fortran/Lisp/Forth/ML - Red, which in turn comes from Rebol first introduced in 1997. But that is one more than I expected to find!
    – Blin
    Dec 13, 2023 at 14:07
  • @Blin: I don't think the design space have been fully explored, I'm sure we will se new ideas in the future. But certain trade-offs are well known, like you can't have a built-in indexing syntax like a[i] and at the same time support defining a variable called a[i]. This is ambiguous however way you slice the cake. So you have to chose one or the other.
    – JacquesB
    Dec 13, 2023 at 14:24

2 Answers 2

13

Short answer: Because programmers want to be able to write 2+2 and f(x), and value this more than being able to use arbitrary symbols and punctuation in variable names.

All mainstream general-purpose languages have a syntax derived from Fortran. Fortran was revolutionary in its day because it allowed you to use regular mathematical notation like:

(2+2)/3

Compared to this, Lisp required you to write

(/ (+ 2 2) 3)

and Forth required you to write

 2 2 + 3 /

Lisp and Forth is much simpler to parse and avoid all the complexities around operator precedence. They also allow a wider range of characters in identifiers, while Fortan-style only allows alphanumeric identifiers.

Nevertheless, I think it is obvious why Fortran-style syntax won out: It allows people to use the mathematical notation they already know.

Fortran-style also have math-like syntax for function invocation f(x) and indexing a[i]. Requiring whitespace separation like f ( x ) and a [ i ] would look weird and detract from the aim of looking like math-notation.

Of course all design decisions have trade-offs. The Fortran style syntax does not allow operator symbols in identifiers, since this would be ambiguous. Only alphanumeric characters are allowed in identifiers.

You suggest that enforcing whitespace around identifiers could allow a larger range of allowed symbols in identifiers without ambiguity. For example if whitespace was enforced, 2 + 2 would be an expression but 2+2 would be an identifier. So you could do stuff like:

int 2+2 = 5;

While this is definitely cool, I'm not sure everybody would consider this an improvement to the readability of code. Most language designers would probably agree that having 2+2 and 2 + 2 mean two entirely different things would be confusing to the average programmer.

Of course Lisp and Forth does allow many more symbols in identfiers, but since these language don't support infix math-like expressions anyway, it is not a source of confusion.

(Some ML-style languages does allow user-defined operators while supporting infix notation. But AFAIK the allowed symbols are more restricted and does not include brackets and letters like in your example.)

2
  • Raku allows defining arbitrary operators with arbitrary precedence (defined relative to other operators), associativity (left, right, none, list, chain), and "fixity" (prefix, postfix, circumfix (i.e parentheses), and postcircumfix (foo[bar])). There are very few restrictions on the allowed characters. E.g. I can define a Circumfix operator with START as the "opening parenthesis" and END as the "closing parenthesis", allowing me to write something like START 1, 2, 3, 4 END. I would call Perl5 "mainstream", but maybe not Raku, though. Dec 11, 2023 at 11:26
  • As you mention, the ML family of languages is an interesting case, it has both infix operators and the function application does not require braces (unless you are doing nested calls). Both x + y is supported and f x y . If the identifier character set was expanded, [1..b] x would be valid and not unusual, and for the nested call case [1..b] ( x + y ) would be valid and not very unusual. Which identifiers can be used in infix calls is a separate problem from which identifiers are possible at all.
    – Blin
    Dec 11, 2023 at 17:32
4

I would hate this. You being able to use [1..b] as an operator isn’t worth my inconvenience at having to not being able to write array[1...b].

So the reason is: Nobody asks for it, nobody wants it, 99% of people would be very unhappy with it.

And the phase in my life where I liked Forth and had it installed on my computer was actually very short.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.