Discussion:
Operator precedence
Add Reply
Janis Papanagnou
2024-05-23 14:13:33 UTC
Reply
Permalink
After reading an old article in the Algol bulletin about questions
and issues with operator precedences I tried this with (Gnu) Awk

$ awk 'BEGIN { a=5; print a^2, -a^2, 0-a^2 }'
25 -25 -25

and shell (Ksh)

$ a=5 ; print $(( a**2 )) $(( -a**2 )) $(( 0-a**2 ))
25 25 -25

and Algol 68

$ a68g -p 'INT a=5; (a^2, -a^2, 0-a^2)'
+25 +25 -25

I don't want to value the different results (based on precedence),
but I'm interested in your comments/thoughts about the differences.

Janis
Kaz Kylheku
2024-05-23 16:49:15 UTC
Reply
Permalink
Post by Janis Papanagnou
After reading an old article in the Algol bulletin about questions
and issues with operator precedences I tried this with (Gnu) Awk
$ awk 'BEGIN { a=5; print a^2, -a^2, 0-a^2 }'
25 -25 -25
and shell (Ksh)
$ a=5 ; print $(( a**2 )) $(( -a**2 )) $(( 0-a**2 ))
25 25 -25
and Algol 68
$ a68g -p 'INT a=5; (a^2, -a^2, 0-a^2)'
+25 +25 -25
I don't want to value the different results (based on precedence),
but I'm interested in your comments/thoughts about the differences.
There is no question that we want the exponentiation operator
to have a higher precedence than plus or minus, even including
the unary forms. It's the "E" in the BEDMAS acronym that
English-speaking children learn in some places in the world: brackets,
exponentiation, division, multiplication, addition, subtracation.

If -a**2 is not parsed as -(a**2), you're messing with the BEDMAS.

Furthermore exponentation between on an intermediate precedence
level between unary minus and regular minus is simply insane.

You might think you're out of the woods with Lisp, where you don't
have precedence. But some math operations have variadic arguments,
so associativity comes into play.

Common Lisp restricts expt to exactly two arguments:

[1]> (expt 5 2)
25
[2]> (expt 5 2 3)

*** - EVAL: too many arguments given to EXPT: (EXPT 2 5 3)

I made it n-ary in TXR Lisp:

1> (expt 5 2)
25
2> (expt 5 2 3)
390625

Look, it's a right-to-left reduction, unlike addition, or multiplication:

1> (expt (expt 5 2) 3)
15625
2> (expt 5 (expt 2 3))
390625

In other words (expt a b c ...) denotes

...
c
b
a

rather than

b c ...
(.. ((a) ) )
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Axel Reichert
2024-05-23 17:14:44 UTC
Reply
Permalink
Furthermore exponentation between on an intermediate precedence level
between unary minus and regular minus is simply insane.
This is a very good argument! Prior to reading it I was kind of
indifferent (at least in infix programming languages, but not in typeset
mathematics).
1> (expt 5 2)
25
2> (expt 5 2 3)
390625
... so conforming to the habit of typeset mathematics, which is based on
the fact that

c
b
a

could be written as

bc
a

if meant to be left-associative. So having it right associative is the
more terse convention.

For further reading,

https://en.wikipedia.org/wiki/Order_of_operations

is informative, while

https://en.wikipedia.org/wiki/Graham%27s_number
https://en.wikipedia.org/wiki/Knuth%27s_up-arrow_notation

are curiosities. Have fun!

Axel
Janis Papanagnou
2024-05-23 23:53:52 UTC
Reply
Permalink
Furthermore exponentation between on an intermediate precedence level
between unary minus and regular minus is simply insane.
This is a very good argument! [...]
I'd be interested why you think so. Unfortunately, and opposed to
the statement, I think there was no actual "argument" presented in
this quoted sentence.[*] - Please don't get me wrong; I'm really
interested in a relatable argument, and I also try to be unbiased!

If I understand Kaz' comment correctly, the ordering of the three
operators should be different? Are you saying that the order
unary minus
exponentiation
binary minus
is somehow "wrong"? - Yet, I don't see why.
Moreover, what seams reasonable (to me) is that _unary_ operators
should (typically/always?) bind tighter than _binary_ operators!
This would imply that exponentiation does not bind higher than the
unary minus. (And that the order of the upthread posted Shell and
Algol 68 expressions would thus fit better.)

Different languages also differ in their implementation choice as
shown; besides languages mentioned I read that (for example) Eiffel
implements it the way that GNU Awk has chosen. - That's now 2:2,
but counting majority is of course also no convincing argument.

OTOH, exponentiation is (typically/always?) right associative, so
it's may be somehow sensible to be handled differently compared to
the other [commutative] binary operators?

You see I'm still undecided, and still looking for explanations.
OTOH, given that this had been discussed by computer scientists
and mathematicians without clear answer, it may boil down to just
be an opinion to be decided in one way or the other.

It is interesting what the [German] Wikipedia[**] says about that.
Specifically that there's (besides exponentiation, multiplication/
division, addition/subtraction) additional categories in programming
languages; the [unary] sign that normally has a yet higher priority
than the exponentiation. - Well...

Janis

[*] "simply insane" doesn't contribute to a substantial argument.

Also the upthread mentioned "BEDMAS" sort of rule doesn't seem
to apply since unary operators are not mentioned (and there's a
difference between subtraction and negation). In our schools we
also learned such simplified rules, in our case it was "Punkt vor
Strich" (meaning multiplication and division comes before addition
and subtraction). And we had also the practice that exponentiation
has a tighter binding than a unary minus sign (as Kaz suggested).

[**] https://de.wikipedia.org/wiki/Operatorrangfolge
Janis Papanagnou
2024-05-24 02:41:11 UTC
Reply
Permalink
After pondering a bit more I think this is my personal resume...
Post by Janis Papanagnou
[...]
Moreover, what seams reasonable (to me) is that _unary_ operators
should (typically/always?) bind tighter than _binary_ operators!
An example may make things probably more apparent; in the expression

5 * -3 ^ 2

it would never occur to me to not consider the "-3" as an entity.
Because of the typical inherently tight coupling of unary operators.

(Assuming an interpretation of

5 * -(3 ^ 2)

would look just wrong to me.)
Post by Janis Papanagnou
This would imply that exponentiation does not bind higher than the
unary minus. (And that the order of the upthread posted Shell and
Algol 68 expressions would thus fit better.)
BTW, Unix 'bc' also behaves that way

$ bc -l <<< " 5 * -3 ^ 2 "
45


<off-topic>
Let me add an Algol 68 anecdote (sort of)... You can of course adjust
the operator precedences in that language to fit your preferences.
And since you have more than one representation for the exponentiation
in Algol 68 - in the Genie compiler for example '^', 'UP', and '**' -
you can even change just one of them (if you like), for example

BEGIN
PRIO UP = 5;
INT a=5;
print ((a ^ 2, - a ^ 2, 0 - a ^ 2, newline));
print ((a UP 2, -a UP 2, 0-a UP 2, newline));
print ((a ** 2, -a ** 2, 0-a ** 2, newline));
SKIP
END

produces
+25 +25 -25
+25 +25 +25
+25 +25 -25

Scary! :-)

Janis
Kaz Kylheku
2024-05-24 04:32:16 UTC
Reply
Permalink
Post by Janis Papanagnou
After pondering a bit more I think this is my personal resume...
Post by Janis Papanagnou
[...]
Moreover, what seams reasonable (to me) is that _unary_ operators
should (typically/always?) bind tighter than _binary_ operators!
An example may make things probably more apparent; in the expression
5 * -3 ^ 2
it would never occur to me to not consider the "-3" as an entity.
Because of the typical inherently tight coupling of unary operators.
(Assuming an interpretation of
5 * -(3 ^ 2)
would look just wrong to me.)
That interpretation is required if - 3 ^ 2 is to correspond to:

2
-3

Indeed the ^ circumflex is intended to mean "we don't have support
for superscripts in this notation, but pretend that the 2 is raised up".

(In TeX/LaTeX, ^ is used for marking up superscripts: x^y.)

Now in the C grammar, we have multiplication also occuping a
precedence rung between unary plus/minus and additive expressions.

Thus -A*B means (-A)*B, whereas X - A*B means X-(A*B).

In this case it doesn't matter because the unary minus is
a kind of product. -A can be regarded as a shorthand for (-1)*A.

More importantly the identity (-A)*B = -(A*B) holds.

This creates a problem if we naively wedge exponentiation into
the grammar, by sticking it into a precedence level above
multiplication, but below unary.

The identity does not hold in exponentiation: (-A)**B
is not -(A**B).
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Axel Reichert
2024-05-25 11:34:25 UTC
Reply
Permalink
Post by Kaz Kylheku
Now in the C grammar, we have multiplication also occuping a
precedence rung between unary plus/minus and additive expressions.
Thus -A*B means (-A)*B, whereas X - A*B means X-(A*B).
In this case it doesn't matter because the unary minus is
a kind of product. -A can be regarded as a shorthand for (-1)*A.
More importantly the identity (-A)*B = -(A*B) holds.
This creates a problem if we naively wedge exponentiation into
the grammar, by sticking it into a precedence level above
multiplication, but below unary.
The identity does not hold in exponentiation: (-A)**B
is not -(A**B).
Very nice explanation, many thanks. I will likely pass it to a friend
(geodesist), who had a heated argument with the math teacher of her son
about whether

2
-3

is -9 or +9. I think the mathematical conventions are clear (and
probably established for centuries), but the teacher might be confused
and think Excel is the reference, see

https://en.wikipedia.org/wiki/Order_of_operations#Unary_minus_sign

It would be interesting to learn about the motivation for spreadsheets
and "bc" to deviate from the mathematical convention.

Best regards

Axel
Janis Papanagnou
2024-05-25 21:31:26 UTC
Reply
Permalink
Post by Axel Reichert
It would be interesting to learn about the motivation for spreadsheets
and "bc" to deviate from the mathematical convention.
It's not only these two program types. As noted upthread it's
also Ksh and Algol 68 (for example), and it seems quite common
in computing according to the cited Wikipedia article.

Janis
Janis Papanagnou
2024-05-25 22:03:02 UTC
Reply
Permalink
Post by Janis Papanagnou
Post by Axel Reichert
It would be interesting to learn about the motivation for spreadsheets
and "bc" to deviate from the mathematical convention.
It's not only these two program types. As noted upthread it's
also Ksh and Algol 68 (for example), and it seems quite common
in computing according to the cited Wikipedia article.
WRT the second question ("deviate from the mathematical convention")
I suppose it's because it's not the best fit for the formalization.

Tight binding of monadic operators appears just natural (to me), so
their "motivation" is quite obvious (to me).

(Mind my remark that mathematicians and computer scientists were
involved in the Algol 68 definition, and that this topic had been
discussed there, and despite the old mathematical convention they
decided to consistently streamline the definition. Frank Pagan's
Algol 68 book, for example says that _every_ monadic operator has
higher precedence than any dyadic operator. And that makes sense;
also in my opinion. I consider Algol 68 also a landmark due to its
extraordinary formal definition, that's why I emphasize it here as
an outstanding paragon.)

(Yet I haven't seen any convincing counter argument. But maybe we
should just agree to disagree, since this topic (as I've read) had
often lead to heated disputes and that's the least I wanted to have
provoked with my post.)

People should be anyway aware of the operator precedences in the
various programming languages since there are obviously yet worse
definitions than the one we've been discussing here.

Janis
Kaz Kylheku
2024-05-25 22:26:40 UTC
Reply
Permalink
Post by Janis Papanagnou
(Mind my remark that mathematicians and computer scientists were
involved in the Algol 68 definition, and that this topic had been
discussed there, and despite the old mathematical convention they
decided to consistently streamline the definition. Frank Pagan's
Algol 68 book, for example says that _every_ monadic operator has
higher precedence than any dyadic operator. And that makes sense;
also in my opinion. I consider Algol 68 also a landmark due to its
extraordinary formal definition, that's why I emphasize it here as
an outstanding paragon.)
What they are effectively saying is that the dyadic power operator A**B
(or A^B or whatever it is) bears no syntactic relation to the 2D notation
involving a superscript. I.e. it is a completely different syntactic
interface to the same abstract operation. As such, it can have its own
precedence rules, not hinged to the superscript power notation.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Janis Papanagnou
2024-05-30 04:59:57 UTC
Reply
Permalink
Post by Kaz Kylheku
Post by Janis Papanagnou
(Mind my remark that mathematicians and computer scientists were
involved in the Algol 68 definition, and that this topic had been
discussed there, and despite the old mathematical convention they
decided to consistently streamline the definition. Frank Pagan's
Algol 68 book, for example says that _every_ monadic operator has
higher precedence than any dyadic operator. And that makes sense;
also in my opinion. I consider Algol 68 also a landmark due to its
extraordinary formal definition, that's why I emphasize it here as
an outstanding paragon.)
What they are effectively saying is that the dyadic power operator A**B
(or A^B or whatever it is) bears no syntactic relation to the 2D notation
involving a superscript. I.e. it is a completely different syntactic
interface to the same abstract operation. As such, it can have its own
precedence rules, not hinged to the superscript power notation.
There's three power operators in Algol 68 ('UP', '^', '**') each with
the same default precedence (and each individually changeable.

What the Algol 68 report says on priorities is:

Priority-declarations are used to specify the priority of operators.
Priorities from 1 to 9 are available.
Since monadic-operators have effectively only one priority-level, which
is higher than that of all dyadic-operators, monadic-operators do not
require priority-declarations.

Formulas are either dyadic or monadic: e.g., x + i or ABS x. The order
of elaboration of a formula is determined by the priority of its
operators; monadic formulas are elaborated first and then the dyadic
ones from the highest to the lowest priority.

https://jmvdveer.home.xs4all.nl/en.post.algol-68-revised-report.html

This is IMO a very clear and sensible definition. It resembles the
property that I upthread mentioned as "tight binding" of monadics.
(I know your mileage varies.)

Janis
Axel Reichert
2024-05-30 08:24:20 UTC
Reply
Permalink
Post by Janis Papanagnou
There's three power operators in Algol 68 ('UP', '^', '**') each with
the same default precedence (and each individually changeable.
As you have mentioned upthread, changing the precedence is scary.
Obfuscated Algol contest ... (-:

Axel
Janis Papanagnou
2024-05-31 15:08:23 UTC
Reply
Permalink
Post by Axel Reichert
Post by Janis Papanagnou
There's three power operators in Algol 68 ('UP', '^', '**') each with
the same default precedence (and each individually changeable.
As you have mentioned upthread, changing the precedence is scary.
Yes, indeed.

OTOH, folks who dislike some precedence (cf. '**', as discussed
here) might then "fix" it to match their own preferences.

Defining precedences for programmer-defined operations is a good
thing, but standard operators for standard expressions (however
they are defined) should be excluded from change; this would be
my preference.

Janis
Christian Weisgerber
2024-05-26 00:37:29 UTC
Reply
Permalink
Post by Janis Papanagnou
People should be anyway aware of the operator precedences in the
various programming languages since there are obviously yet worse
definitions than the one we've been discussing here.
Which is why BSD has had an operator(7) man page about "C operator
precedence and order of evaluation" since 1990.
--
Christian "naddy" Weisgerber ***@mips.inka.de
Kaz Kylheku
2024-05-26 02:06:40 UTC
Reply
Permalink
Post by Christian Weisgerber
Post by Janis Papanagnou
People should be anyway aware of the operator precedences in the
various programming languages since there are obviously yet worse
definitions than the one we've been discussing here.
Which is why BSD has had an operator(7) man page about "C operator
precedence and order of evaluation" since 1990.
This discussion has convinced me that unary operators must not be
clumped together and put into a single precedence level.

For instance, if we have the C-like expression

-*p/*q

it should ideally be

-(*p/*q)

and not as it is now:

(-*p)/(*q)

due the unary minus being clumped with dereference.

Unary minus should not be a distinct operator from binary minus.
Unary minus should denote the elision of an identity element term,
so that - X not only means the same as 0 - X, but is considered
to be the same notation, just with the additive identity element not
show. We could correctly parse it as a binary minus by putting the
element back in.

This can make a difference easily. Consider that the unsigned types
in C have a definition for unary minus. If *p is of type unsigned
int, and its value is 1, then (-*p) is UINT_MAX. *q is 4 then
we get UINT_MAX / 4. Under the proposedd rule, we would get
something else: 1/4 producing 0, and that negating to 0.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Janis Papanagnou
2024-05-30 05:26:11 UTC
Reply
Permalink
Post by Kaz Kylheku
Post by Christian Weisgerber
Post by Janis Papanagnou
People should be anyway aware of the operator precedences in the
various programming languages since there are obviously yet worse
definitions than the one we've been discussing here.
Which is why BSD has had an operator(7) man page about "C operator
precedence and order of evaluation" since 1990.
This discussion has convinced me that unary operators must not be
clumped together and put into a single precedence level.
That's the way it goes. With proceeding discussion and studies of
yet more literature sources I came exactly to a different result,
and confidence in my (initially not that strong) view.
Post by Kaz Kylheku
For instance, if we have the C-like expression
-*p/*q
it should ideally be
-(*p/*q)
(-*p)/(*q)
due the unary minus being clumped with dereference.
I won't comment on the dereferencing operator in C; it's a low
level construct of a low level language that you don't find in
Algol (the language mentioned as reference) or Awk (the language
this NG is dedicated to), nor in 'bc' or spreadsheets, I suppose,
which have also been mentioned.

A note on the upthread also mentioned Ksh (which also shows this
precedence). Earlier versions of Ksh did not support '**' in
arithmetic expressions, it was added later. And (as opposed to
C's misplaced &, |, and ^ operators) they deliberately inserted
it between dyadic and monadic '-', which makes me confident that
they thought well about the appropriate place where to insert it.
(But this is of course just a detail, not a formal reasoning.)
Post by Kaz Kylheku
Unary minus should not be a distinct operator from binary minus.
This is a statement I really find scary.
Post by Kaz Kylheku
Unary minus should denote the elision of an identity element term,
so that - X not only means the same as 0 - X, but is considered
to be the same notation, just with the additive identity element not
show. We could correctly parse it as a binary minus by putting the
element back in.
But this reasoning is just artificial. Only because we can use
transformations 0-X or -1*X for -X that doesn't mean that a
subtraction is the same as a negation.

We have dedicated syntax trees for monadic and dyadic operations.
Post by Kaz Kylheku
This can make a difference easily. Consider that the unsigned types
in C have a definition for unary minus. If *p is of type unsigned
int, and its value is 1, then (-*p) is UINT_MAX. *q is 4 then
we get UINT_MAX / 4. Under the proposedd rule, we would get
something else: 1/4 producing 0, and that negating to 0.
(Again low level C.)

Janis
Janis Papanagnou
2024-05-30 05:06:23 UTC
Reply
Permalink
Post by Christian Weisgerber
Post by Janis Papanagnou
People should be anyway aware of the operator precedences in the
various programming languages since there are obviously yet worse
definitions than the one we've been discussing here.
Which is why BSD has had an operator(7) man page about "C operator
precedence and order of evaluation" since 1990.
And this is why my old K&R book had got from the beginning a
bookmark affixed at the page with the table of operators. :-)

Janis
Axel Reichert
2024-05-30 08:22:01 UTC
Reply
Permalink
Post by Janis Papanagnou
People should be anyway aware of the operator precedences in the
various programming languages
Sure. I have neither problems with prefix notation as in Lisp or postfix
notation as in Factor. It is the infix languages (which boast as an
advantage to be closer the usual mathematical notation) where I think it
is crucial to stay close to the mathematical notation, otherwise the
purported advantage vanishes immediately.

Axel
Janis Papanagnou
2024-05-31 15:16:32 UTC
Reply
Permalink
[...] It is the infix languages (which boast as an
advantage to be closer the usual mathematical notation) where I think it
is crucial to stay close to the mathematical notation, otherwise the
purported advantage vanishes immediately.
Obviously there's other serious opinions differing from this view,
as we've seen. (It's certainly more comfortable to stay with old
conventions. Sometimes it's necessary to jettison something if it
got inappropriate or if there's new aspects that changes weight.)

Janis
Janis Papanagnou
2024-05-25 21:31:54 UTC
Reply
Permalink
Post by Kaz Kylheku
Post by Janis Papanagnou
After pondering a bit more I think this is my personal resume...
Post by Janis Papanagnou
[...]
Moreover, what seams reasonable (to me) is that _unary_ operators
should (typically/always?) bind tighter than _binary_ operators!
An example may make things probably more apparent; in the expression
5 * -3 ^ 2
it would never occur to me to not consider the "-3" as an entity.
Because of the typical inherently tight coupling of unary operators.
(Assuming an interpretation of
5 * -(3 ^ 2)
would look just wrong to me.)
2
-3
Indeed the ^ circumflex is intended to mean "we don't have support
for superscripts in this notation, but pretend that the 2 is raised up".
(In TeX/LaTeX, ^ is used for marking up superscripts: x^y.)
Now in the C grammar, we have multiplication also occuping a
precedence rung between unary plus/minus and additive expressions.
Thus -A*B means (-A)*B, whereas X - A*B means X-(A*B).
In this case it doesn't matter because the unary minus is
a kind of product. -A can be regarded as a shorthand for (-1)*A.
More importantly the identity (-A)*B = -(A*B) holds.
So far so good, only it doesn't (IMO) provide a rationale for
exponentiation.
Post by Kaz Kylheku
This creates a problem if we naively wedge exponentiation into
the grammar, by sticking it into a precedence level above
multiplication, but below unary.
The identity does not hold in exponentiation: (-A)**B
is not -(A**B).
Looks like a non-sequitur to me.

Janis
Kaz Kylheku
2024-05-26 01:29:11 UTC
Reply
Permalink
Post by Janis Papanagnou
Post by Kaz Kylheku
This creates a problem if we naively wedge exponentiation into
the grammar, by sticking it into a precedence level above
multiplication, but below unary.
The identity does not hold in exponentiation: (-A)**B
is not -(A**B).
Looks like a non-sequitur to me.
Yes; the only point here is to explain why we don't notice this
issue, which is mirrored in multiplication: Given -A*B,
we can cheerfully stick a 0 on it to make 0-A*B. The value
does not surprisingly change. Yet, the underlying precedence
relationships are parallel to those in -A**B vs 0-A**B, where
there isn't an identity which masks the issue.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Axel Reichert
2024-05-30 08:17:04 UTC
Reply
Permalink
Post by Janis Papanagnou
Are you saying that the order
unary minus
exponentiation
binary minus
is somehow "wrong"?
It certainly feels like this to me. When thinking about the "why", I
could see two "arguments":

1. Unary and binary minus, while not identical operators, at least
visually are identical. Hence it reduces the cognitive load on the
reader of source code, who does not need to remember that unary minus
and binary minus are "on opposite sides of the 'exponentiation fence'".

2. Mathematical programming is algorithms (often beautifully typeset
with (La)TeX) transformed into more mundane ASCII-text representations
(which I see as a technical limitation). Using different operator
precedence depending on the visual representation is certainly confusing
if not dangerous (error-prone coding). Also, I do not see the benefit of
deviating from the mathematical convention.

With the rather new trend to use UTF characters in source code (lambda
and other greek letter, arrows) the visual distinction between printed
math and written source gets smaller, so it makes less and less sense to
use different conventions for the two.

Best regards

Axel
Janis Papanagnou
2024-05-31 15:46:30 UTC
Reply
Permalink
Post by Axel Reichert
Post by Janis Papanagnou
Are you saying that the order
unary minus
exponentiation
binary minus
is somehow "wrong"?
It certainly feels like this to me. When thinking about the "why", I
1. Unary and binary minus, while not identical operators, at least
visually are identical. Hence it reduces the cognitive load on the
reader of source code, who does not need to remember that unary minus
and binary minus are "on opposite sides of the 'exponentiation fence'".
I sort this "argument" (quotes borrowed from your text) into the same
category as "[old] mathematical convention". IMO it doesn't withstand
the slightest analysis. To explain I'm going back to "Computer Science
101" as taught in the 1980's (and probably even before)...
You model expressions with Kantorovic trees (using a homonym '-' here)
a b c
\ / |
'-' '-'
but what I depicted are different operations, as can obviously be seen.
Their evaluation in a stack automaton will happen like
push(a); push(b); subtract() and push(c); negate() respectively.
If there wouldn't be a distinction and we had, say, a single 'minus()'
operation there'd be no indication to reduce that part of expression.

Same glyph doesn't tell anything in an area (mathematics, but also CS)
that is meanwhile highly overloaded with symbols.

In CS we have even more complex situation depending on involved types.
'**' is a good example also here; 'a**b' is depending on the involved
types; try (for example in Awk)
c = 2 ; d = 2.0 ; e = 2.0000000001 ; f = -2.3
print f^c, f^d, f^e
There's not one '**' function but actually two under one name
exp: numeric x int -> numeric
exp: numeric x real -> real
Post by Axel Reichert
2. Mathematical programming is algorithms (often beautifully typeset
with (La)TeX) transformed into more mundane ASCII-text representations
(which I see as a technical limitation). Using different operator
precedence depending on the visual representation is certainly confusing
if not dangerous (error-prone coding). Also, I do not see the benefit of
deviating from the mathematical convention.
In the first part you seem to just repeat your "argument" 1. above.

Deviation from conventions makes sense when appropriate. (This had
been discussed with examples and I think it's worth pondering about.
I'm glad that the "Burning Witches" convention has been superseded,
and that "Newton" got fixed by Relativity and Quantum Mechanics. :-)

Janis
Post by Axel Reichert
With the rather new trend to use UTF characters in source code (lambda
and other greek letter, arrows) the visual distinction between printed
math and written source gets smaller, so it makes less and less sense to
use different conventions for the two.
Best regards
Axel
Axel Reichert
2024-06-01 07:11:17 UTC
Reply
Permalink
Post by Janis Papanagnou
Post by Axel Reichert
1. Unary and binary minus, while not identical operators, at least
visually are identical. Hence it reduces the cognitive load on the
reader of source code, who does not need to remember that unary minus
and binary minus are "on opposite sides of the 'exponentiation fence'".
I sort this "argument" (quotes borrowed from your text) into the same
category as "[old] mathematical convention". IMO it doesn't withstand
the slightest analysis. To explain I'm going back to "Computer Science
101" as taught in the 1980's (and probably even before)...
You model expressions with Kantorovic trees (using a homonym '-' here)
a b c
\ / |
'-' '-'
but what I depicted are different operations, as can obviously be seen.
Their evaluation in a stack automaton will happen like
push(a); push(b); subtract() and push(c); negate() respectively.
If there wouldn't be a distinction and we had, say, a single 'minus()'
operation there'd be no indication to reduce that part of expression.
How does the low-level computational model of a stack automaton
invalidate my claim about the reduced cognitive load? When reading
source code (in an supposedly infix language), I certainly think neither
about Kantorovic trees nor about an Forth-like (RPN) evaluation model.
Post by Janis Papanagnou
Deviation from conventions makes sense when appropriate.
Sure. If you can agree with the summary that the disagreement is about
"conventions copied from the domain modelled, in this case maths (awk,
...) versus consistency (Algol, ksh)", than we can end this (very
interesting!) discussion.

By the way, of the latter (consistency), I am a big fan, hence some
programming language's syntax excessions drive me crazy: "Was it
'foo(bar)' or 'bar.foo()' or 'foo bar'?" Which is why I have a weak spot
for Lisps and Forth/Factor. No more earmarks in books with operator
Post by Janis Papanagnou
I'm glad that the "Burning Witches" convention has been superseded,
and that "Newton" got fixed by Relativity and Quantum Mechanics. :-)
I know what you mean, but rest asured that Newton has NEITHER been
wronged NOR superseded in the domain relevant to daily life. I would
have used the wording "extended" rather than "fixed", but can live with
your choice. (-:

Thanks and best regards!

Axel

Kaz Kylheku
2024-05-24 03:45:50 UTC
Reply
Permalink
Post by Axel Reichert
Furthermore exponentation between on an intermediate precedence level
between unary minus and regular minus is simply insane.
This is a very good argument! Prior to reading it I was kind of
indifferent (at least in infix programming languages, but not in typeset
mathematics).
1> (expt 5 2)
25
2> (expt 5 2 3)
390625
... so conforming to the habit of typeset mathematics, which is based on
the fact that
c
b
a
could be written as
bc
a
if meant to be left-associative. So having it right associative is the
more terse convention.
Yes. Also, in the left associative semantics, we have a choice. We can
do the exponentation earnestly as if by:

(expt (expt 5 2) 3)

Or we can take advantage of the identity:

(expt 5 (* 2 3))

The former could be important in some situation involving
floating-point: you can't always use simplifying identities.

The latter is favored when efficiency is more important.

A left associative n-ary expt function would have to make one of those
two choices: either doggedly do the exponentiations (such that the
function would be avoided by anyone looking for efficiency), or else
serve as a syntactic sugar for the exponent raised to the product
of the remaining arguments (which provides low value as a syntactic
sugar, and yet has to be avoided by someone who can't use that identity
for whatever reason).

I felt that the n-ary semantics of expt provided the best value
when right associative. It's not something obtainable by a ready
identity, and there is one obvious way to do it.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Axel Reichert
2024-05-25 11:09:35 UTC
Reply
Permalink
in the left associative semantics, we have a choice. We can do the
(expt (expt 5 2) 3)
(expt 5 (* 2 3))
The former could be important in some situation involving
floating-point: you can't always use simplifying identities.
I cannot readily imagine a scenario in which simplifying is not possible
or is disadvantageous. Could you please elaborate?
I felt that the n-ary semantics of expt provided the best value
when right associative. It's not something obtainable by a ready
identity, and there is one obvious way to do it.
Full ACK.

Best regards

Axel
Kaz Kylheku
2024-05-25 22:20:42 UTC
Reply
Permalink
Post by Axel Reichert
in the left associative semantics, we have a choice. We can do the
(expt (expt 5 2) 3)
(expt 5 (* 2 3))
The former could be important in some situation involving
floating-point: you can't always use simplifying identities.
I cannot readily imagine a scenario in which simplifying is not possible
or is disadvantageous. Could you please elaborate?
I'm simply not enough of a numerical analyst to be able to rule such a
situation as nonexistent.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Loading...