Allowing identifier to begin with digit

Hi there,

Currently, identifier-head doesn't include decimal-digit but what if it's allowed to use when it's escaped with backtick?

Maybe it doesn't make much sense for identifier itself but how about enum-case-name? People often use underscore to overcome the restriction but why can't we allow it with backtick as below?

// currently not allowed
enum Accuracy {
case `500m`
case `1km`
}
set(accuracy: .1km)

I think this can be very convenient and really neat.

Since we can't write .1 to mean 0.1, I believe this isn't too confusing. What do you think?

10 Likes

As long as the identifier doesn't only contain digits, right?

I'm more inclined to prevent identifiers from starting with a digit.

It's true that we have a precedent with tuples, since they get numeric identifiers for each property place (e.g. (1, 2).0 and (1, 2).1), but that's the only exception to the rule.

Since my field of expertise is mathematics, I would love the ability to define custom infix operators between numeric literals and variables with a blank identifier, in order to write modules having:

// strawman syntax
infix func ``(lhs: IntegerLiteral, rhs: Int) -> Int {
  return lhs as Int * rhs
}

let x = 10
let y = x^2 - 4x + 4  // 4x is 4 * x

infix func ``(lhs: FloatLiteral, rhs: Complex) -> Complex {
  return lhs as Complex * rhs
}

let i: Complex = (0, 1)
let z = 3 + 2i  // 2i is 2 * i, i.e. (0, 2)

With your suggestion in place, this wouldn't be potentially possible anymore (well.. it's not possible even now since there should be a way to disambiguate 0x2p5 which is 64 written in hexadecimal notation, but I won't let my hopes fade away that easily~).


Numeric identifiers could be spelled between backticks where required, so those shouldn't be problematic:

let `1` = 1

1    // refers to the integer literal
`1`  // refers to the variable `1`

struct TupleLike<A, B> {
  let `0`: A
  let `1`: B

  init(_ a: A, _ b: B) {
    `0` = a  // or self.0 = a
    `1` = b  // or self.1 = b
  }
}

let tuple = TupleLike("one", "two")
tuple.0  // is "one"
tuple.1  // is "two"

Please see this rejected proposal first: SE-0275: Allow more characters (like whitespaces and punctuations) for escaped identifiers - #46 by Joe_Groff

I think this one should address the review feedback.

2 Likes

True... but that rejection also specifically leaves the question of numeric-starting identifiers open for further discussion:

(emphasis added) To me, it seems like this proposal would fall under the "more concrete work" banner. :slight_smile:

2 Likes

“...more concrete work done on libraries that would benefit from this functionality, to better gauge whether it is really essential...”

Concrete work is here used in contradistinction to proposals, which are not libraries.

1 Like

every time i have run into this issue i’ve just done the simple thing and prefixed with an underscore:

enum Accuracy 
{
case _500m 
case _1km 
}

it is ugly but surely not as ugly as having to write the backticks every time.

sometimes if it’s a name i am going to use a lot, i just spell it backwards:

enum Texture 
{
    struct D2 { ... } // 2-dimensional texture
    struct D3 { ... } // 3-dimensional texture
}

this really doesn’t seem like a motivating argument against this pitch. multiplication should only be written one way, and in Swift, that way is with the * operator. allowing things like 2x to mean 2 * x just makes your code harder to understand.

2 Likes

Yeah, I do this too. If this pitch makes it through evolution (and I hope it does!), I don't think we'd need to "write the backticks every time", because we already don't have to:

class Multipleton {
    static let `default` = Multipleton() // backticks required because "default" is a keyword
    init() { ... }
}

let instance = Multipleton.default // no backticks required
let instance: Multipleton = .default // also acceptable
8 Likes

I also use the convention of prefixing with an underscore.

From one of my apps:

enum SampleRate: Int {
	case _48kHz = 48
	case _96kHz = 96
	case _192kHz = 192
}
1 Like

I think digits only identifier is nothing wrong.

var `42` = Double.pi  // this looks silly but
var `false` = true    // this is allowed.

And I think HTTPStatus.404 and SFSymbols.10.circle are pretty neat.

The current "convention" of using underscore prefix is only because you can't do `404` is my understanding

If the only question is whether this feature is "really essential" or "merely nice-to-have", I think this idea may not have any chance, but I personally don't feel using underscore prefix to overcome the current restriction is really a great idea especially now Property Wrapper feature magically uses it.

1 Like

If it'd be allowed, do you think you'll go with:

enum SampleRate: Int {
	case `48kHz` = 48
	case `96kHz` = 96
	case `192kHz` = 192
}

I happened to choose spelling them out as below for a library I wrote because I'm not a big fan of underscore prefix "convention".

enum Precision: Int {
    case twentyFiveHundredKilometers = 1
    case sixHundredThirtyKilometers
    case seventyEightKilometers
}

But I'd have definitely gone with below, if it was allowed.

enum Precision: Int {
    case `2500km` = 1
    case `630km`
    case `78km`
}
1 Like

A natural but currently impossible example:

enum Dimensions {
  case 2D
  case 3D
}

I would love to see the support of identifiers starting with digits.

3 Likes

Should we allow numerics only in enum cases, or also in variable and function argument names?

My personal opinion is that we should allow it in variable names and function arguments, but the name must contain at least one alphabet symbol.