Multiplication by juxtaposition

Multiplication by juxtaposition

Proposal: SN–NNNN
Author: @Nevin
Status: Ready to go

Introduction

We propose to allow multiplication to be written by juxtaposition, as in “n(n+1)”.

Motivation

One of the goals of the Swift Numerics package is to make it easier to write mathematical code in a familiar and easy-to-understand way. Or at least, the authors of this proposal assume that’s a goal. We didn’t actually check.

Anyway, it is common practice in mathematical writing to denote multiplication by justaposition. For example, “2x” or “(x+1)(x-1)”. The first form, without parenthesis, is prone to ambiguity in Swift, because for example 2e-4 represents a floating-point literal, and it would be source-breaking to parse it as 2 * e - 4 instead. Thus, for the current pitch, we require the second term to be enclosed within parentheses.

Proposed solution

We propose to allow multiplication by juxtaposition in Swift. This will allow people to write simple, easy-to-read code such as:

let y = 2(x+1)
let z = (x+1)(x-1)
let crossRatio = (a-c)(b-d) / (b-c)(a-d)

Notice, in that last example, the multiplication in the denominator has higher precedence than the division. We consider this a desirable feature, as it matches the way people naturally write and understand expressions of this form.

Moreover, multiplication by juxtaposition has higher priority than any other operation. Users who want a lower precedence should write out the multiplication operator as they currently do.

Detailed design

This feature can be implemented in Swift as it exists today. No changes to the language are necessary. For edification purposes we present a complete, working implementation here. The particular details of this implementation are not salient to the proposal, so we have hidden the code to keep the length of this post manageable:

Implementation of multiplication by juxtaposition
extension Numeric {
  func callAsFunction(_ x: Self) -> Self {
    return self * x
  }
}

Source and ABI compatibility

This is an additive feature.

Future directions

As mentioned above, this feature currently requires that the second term of a product be enclosed in parentheses. We are hopeful that this limitation might be lifted in the future, to allow expressions like 2x and (n+1)n. More precisely, we hope that someone else can figure out how to lift the restriction, because we ain’t got no freakin’ clue.

Alternatives considered

We considered the alternative of not adding this feature, but, like, why wouldn’t we?

11 Likes

Playing it straight:

Julia is the most successful language I know of that has actually done this somewhat thoughtfully. IIRC they permit only the following cases:

  • [numeric literal][identifier]
  • [numeric literal][parenthesized expression]
  • [parenthesized expression][identifier]

Of note, they permit (x+1)x, but not (x+1)(x-1), precisely to avoid ambiguity with function application (callAsFunction in Swift). This matters when working with types like quaternions, where q(v) would often refer to the action of the quaternion by conjugation rather than the quaternion-vector product qv.

7 Likes

You can do this without any new language changes using result builders.

let x: @Multiply () -> Int = {
  (x+1)
  (x-1)
}
7 Likes

+1 sounds good to me.

This is more colloquially known as "implicit multiplication", and it's very feasible to implement. My math parsing library does this as part of interpreting tokens. My implementation is a little simpler, because by the time I inject the implicit multiplication token, I already know if I'm dealing with a variable token or a potential "function name" token; I also assume that an identifier followed by an open parenthesis is always a function call.

2 Likes

This is a good start but it doesn't go far enough.

If you juxtapose a sequence of superscript numerals to the right of another Numeric term, the compiler should lower that to a type-appropriate exponentiation:

let x = 0.5²  // let x = pow(0.5, 2)
let y = 2¹⁰   // let y = 1 << 10

Those are all currently lexed as identifiers, but Swift 6 would be the perfect time to source-break and treat them as a special kind of numeric token instead.

10 Likes

I take it that space is allowed between terms and (with or without space) 1/2 (3) would give 1/6 under this proposal (vs 3/2 if there was an explicit *)?

We could also consider addition by juxtaposition: 9 3/4.

1 Like