9
$\begingroup$

C does not have an exponentiation operator, so that freestanding implementations that lack the math libraries could lack exponentiation as a whole, and still be permissible by the C standard. As a result, we have a pow() function for exponentiation and ^ for XOR, which any reasonable system has.

But many languages, even those without a concept of freestanding implementations that lack most of the standard library, followed in the footsteps of C, and as a result, lack an exponentiation operator, a function which ^ would intuitively have.

Dropping support for systems that lack exponentiation capabilities, what syntax choices do I have to implement an exponentiation operator?

$\endgroup$
5
  • 1
    $\begingroup$ The question title asks about implementation (in which case they are unrelated, and just two different questions, but both are relatively trivial). But it seems from the question body that you want to know about syntax choices, not how to implement these operators. I'm not sure what "But many languages, even those without a concept of freestanding implementations that lack most of the standard library" means. $\endgroup$
    – kaya3
    Commented Jul 3, 2023 at 21:40
  • $\begingroup$ @kaya3-supportthestrike C has this concept of 'freestanding implementation' which basically means a C implementation that lacks most of the standard library. $\endgroup$
    – CPlus
    Commented Jul 3, 2023 at 21:42
  • 1
    $\begingroup$ OK. Well, I don't see what that has to do with what the syntax choices for exponentiation are. It seems to me that the question could be written simply as, "what syntax options are there for exponentiation, assuming that ^ is reserved for the XOR operator?" $\endgroup$
    – kaya3
    Commented Jul 3, 2023 at 21:43
  • $\begingroup$ @kaya3-supportthestrike Explaining the rationale for many languages not having an exponentiation operator. That's all. $\endgroup$
    – CPlus
    Commented Jul 3, 2023 at 22:17
  • $\begingroup$ See also What syntax could infix method calls use?. $\endgroup$
    – BoppreH
    Commented Jul 4, 2023 at 0:31

6 Answers 6

7
$\begingroup$

Lua solves this by using x ~ y for XOR.

When applied to floats which do not have an integer representation, it throws an error.

This is reasonably intuitive, since Lua uses ~x for bitwise negation (which is pretty common since the C days), and bitwise negation is just a special case of XOR (~x == (~0) ~ x). This also avoids having to introduce a new operator symbol.

(converted from a comment to an answer)


Other, more obscure ASCII sequences that come to mind are:

  • <>, since XOR can be seen as a bitwise antivalence (downside: many programming languages use this for ~= / !=)
  • (+), since XOR can be seen as a bitwise addition mod 2, which is usually notated as "oplus"
$\endgroup$
3
  • $\begingroup$ Can it also implement an exponentian operator? $\endgroup$ Commented Jul 6, 2023 at 9:54
  • 1
    $\begingroup$ @Starship-OnStrike not sure what you're asking, but Lua uses ^ for exponentiation. $\endgroup$
    – Luatic
    Commented Jul 6, 2023 at 9:57
  • $\begingroup$ Then I suppose thats a yes, sorry for the stupid question $\endgroup$ Commented Jul 6, 2023 at 9:58
19
$\begingroup$

Demote xor

Unless you're in C or a similarly low-level language, you're not going to be doing a lot of bit-level manipulation. So the bitwise operations really don't need to be using up those juicy one-character infix symbols that we have a very limited supply of. In a high-level language, use ^ for the common case of exponentiation, and supply bitwise-xor (and the other bitwise operations, for that matter) as a named function or an operator with a longer name.

  • Haskell supplies xor as a standard function, not an operator. Haskell also demotes the other bitwise operators to have longer names (.&. and .|.), leaving & available for a higher-order function, | available as syntax, and ^ for exponentiation.
  • Julia goes even further, relegating bitwise-xor to the Unicode symbol (with the function xor as an alias), and using ^ for exponentiation.
$\endgroup$
13
  • 3
    $\begingroup$ As someone who uses a lot of xor's, I have to agree. If the problem calls for xor, the user is a programmer and will know how to find it. $\endgroup$
    – BoppreH
    Commented Jul 4, 2023 at 0:27
  • 1
    $\begingroup$ I use bitwise arithmetic all the time. $\endgroup$
    – CPlus
    Commented Jul 4, 2023 at 2:09
  • 10
    $\begingroup$ Lua solves this by using a ~ b for XOR and ~a for negation, which is intuitive since negation is just 0 ~ a. $\endgroup$
    – Luatic
    Commented Jul 4, 2023 at 8:30
  • 1
    $\begingroup$ @Luatic by negation do you mean bitwise inversion? For two's complement numbers producing a negative number from a positive one would require one more step. $\endgroup$ Commented Jul 4, 2023 at 15:15
  • 1
    $\begingroup$ @MarkRansom Yes, unary ~ in Lua performs bitwise negation, not arithmetic negation (which uses - like most mainstream languages) $\endgroup$ Commented Jul 4, 2023 at 15:15
16
$\begingroup$

Double star

Many languages have a ** operator for exponentiation. This is reasonably intuitive, as exponentiation is just the hyperoperation after multiplication.

^ for floating-point only

Floating point types do not have bitwise operators, so the ^ operator could mean exponentiation if the operands are floating-point, but still mean XOR if the operands are integers. This could be implemented the same way as pow() as the function is already for floating-point only, as there is no pow() for integers in C.

$\endgroup$
5
  • $\begingroup$ ^^ could also work, no? $\endgroup$
    – Adám
    Commented Jul 3, 2023 at 21:47
  • 24
    $\begingroup$ So 2.0^3 is 8, but 2^3 is 1? That would easily cause confusion. $\endgroup$
    – dan04
    Commented Jul 3, 2023 at 22:02
  • 5
    $\begingroup$ +1 for the double star, -1 for the floating-point-only solution. Is it even implemented anywhere? $\endgroup$
    – BoppreH
    Commented Jul 3, 2023 at 23:12
  • 4
    $\begingroup$ Haskell uses ^ for non-negative integral power, ^^ for possibly-negative integral power, and ** for floating-point power. Btw I agree that overloading the same operator ^ for integer XOR and floating power is probably not a good idea. $\endgroup$
    – Bubbler
    Commented Jul 3, 2023 at 23:28
  • 2
    $\begingroup$ python3 does ** and i like python3 so, there is my very biased comment why i believe this is a good answer $\endgroup$ Commented Jul 5, 2023 at 0:51
16
$\begingroup$

I think most of the problem comes from functions being in the form op(a, b), which is verbose and doesn't read well when expressions get complicated.

So why not use the word pow, but as an infix operator?

Most of Python's boolean operators are words:

if a or b and not c:
    pass

(also in C/C++ with the right flags, but you'll get yelled at)

And Haskell has a feature where arbitrary functions can be made infix by wrapping them in backticks. You can use it for either pow, xor, or both:

2 `pow` 3
2 `xor` 3

It's fairly annoying if the operator is frequently used (e.g., xor in cryptography), but the readability is very good and mistakes are unlikely.

Of course, you can pick other signifiers instead of double backticks:

2 \pow 3
2 @pow 3
2 $pow 3
2 `pow 3

Or, if your data model and syntax allows it, as a method on number types, which has the same order as an infix operator:

2.pow(3)
$\endgroup$
3
  • 4
    $\begingroup$ There's precedent for infix "word" operators even in languages without custom operator names, e.g., Pascal's div and mod for integer division. $\endgroup$
    – dan04
    Commented Jul 3, 2023 at 23:32
  • $\begingroup$ @dan04 Right, I forgot about that. Python is another obvious example. I'll edit the answer, thanks. $\endgroup$
    – BoppreH
    Commented Jul 3, 2023 at 23:40
  • $\begingroup$ And languages such as Scala and Kotlin allow infix function calls. $\endgroup$
    – gidds
    Commented Jul 5, 2023 at 14:22
7
$\begingroup$

Unicode

See for examples:

Powers

Superscript for powers

23

Downsides:

  • It seems this has to be implemented as a style. There is no simple unicode combiner trick.

  • That gives it the same risks as giving semantic meaning colour or italics but you could if you were willing to accept <sup> in an ASCII version of your code.

  • It could also get messy if you want nested powers like 234

Based on that I have to prefer the simple obvious and (not wrong) suggestion of:

Double star

As suggested in https://langdev.stackexchange.com/a/1960/285

Many languages have a ** operator for exponentiation. This is reasonably intuitive, as exponentiation is just the hyperoperation after multiplication.

Though there is no reason a clever IDE couldn't render that as a superscript I suppose but it might cause some cognitive stress.

Prior art

As helpfully pointed out in response to my question that wikipedia mentions:

  • x ↑ y: Algol Reference language, Commodore BASIC, TRS-80 LevelII/III BASIC.[45][46]
  • x ^^ y: Haskell (for fractional base, integer exponents), D.
  • x⋆y: APL.

The up arrow has other meanings including the next hyperoperation up from exponentation.

APLs use of a single star is bound to cause confusion for users thinking in ASCII. APL uses × for multiplication.

Logic

veebar or oplus for xor

⊻ ⊕


consider IDE support

You could have your IDE do things like automagically convert ** to superscript and v_ to ⊻ say. It would not surprise me if there are relative common key shortcuts for these already and they are just not more widely known.

It is obligatory to remember trigraphs at this point and note that they have only recently been purged from languages like C++.

Off topic but it would be nice to be able to standardise on something like:

Defining a "meta key" for maths and one key for each common symbol so you don't have to type the Unicode in full.

$\endgroup$
5
4
$\begingroup$

** is fairly common. Speaking personally, I strongly dislike "identifiers that parse like operators" of which the most notorious is Pascal's inline mod. Unicode is best avoided at least in a base language (i.e. it would be a nice thing to be able to construct custom Unicode operators on a project- or site-specific basis).

I think the best solution would be to make it strongly depend on the type of the operands, i.e. to distinguish between bytes etc. which can be xored, anded etc. and integers which can be added and multiplied. In fact I came across this a year or so ago written as (in part) a criticism of APL's odd parsing:

...one need only deal with operators that have the same kind of operand, i.e. that have the same domains. For example, A + B > C must imply comparing A + B with C, because A + B gives a numerical sresult whereas B > C gives a logical result and one cannot add a number and a logical value, despite APL and PL/1 pretending that one can. There is, therefore, no need to establish relative precedences for + and >. P.A.Samet, UCL, circa 1975.

https://onlinelibrary.wiley.com/doi/abs/10.1002/spe.4380050310

My holy grail is a moderately efficient parser which can handle the ambiguities relating to monadic/dyadic ^ (dereference, xor, exponentiation) and monadic ! (negation, factorial) as well as Smalltalk-style keywords.

Looking back through some notes, ! is an interesting case particularly if we would also like it to be a dyadic operator to suit e.g. APL's combination operator.

x!       x factorial
!y       y negated, only valid if y is a boolean
!!z      Integer z converted to a boolean
a ! b   Number of combinations of b taken in groups of a
a! b    Can't be postfix factorial, lacks a dyadic operator
a !b    Can't be prefix negation, and b not a boolean
a !!b   Invalid, lacks a dyadic operator
a! ! b  Number of combinations of b taken in groups of a! (a factorial)

What I concluded from that type of exercise is that a character could be used for a fairly wide mix of operators including type-dependent local definitions, but that the situation could be improved enormously if there were an initial (i.e. project level) declaration of what characters and character sequences should be considered valid operators with association rules etc. and that definitions were not allowed to vary this.

$\endgroup$
4
  • $\begingroup$ Is your proposal to have no types that support both bitwise and arithmetic operations? So bithacks such as clearing the lowest set bit (x &= x-1 in C) would need casts to different types for the -1 and the &? $\endgroup$ Commented Jul 4, 2023 at 23:34
  • $\begingroup$ Well, it's just a "thought experiment" at the moment but I think it's a question of favouring the most common usage for each operation. In addition, once the parser has chosen the correct operation the optimiser should be able to eliminate any overhead introduced by casting. $\endgroup$ Commented Jul 5, 2023 at 8:04
  • 1
    $\begingroup$ ** doesn't work well particularly well for C or C++, because * as a unary operator (dereference) can be needed multiple times (unlike unary + or -). But it is indeed common in languages without a unary * operator. $\endgroup$ Commented Jul 5, 2023 at 9:44
  • $\begingroup$ I can assure you that C is the last thing on my mind :-) Besides, which, successive dereferences work perfectly well if the referent is itself a pointer: the real problem in this area is languages like Object Pascal (as implemented by Delphi) which allow the unary dereference operator (^ in this case) to be omitted under certain circumstances. $\endgroup$ Commented Jul 5, 2023 at 13:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .