E11064: literal in comparison/range - give it a meaningful name

Category: E11xxx Code quality and IR analysis · Reported in phase: PRE_IR_CHECKS · Classification: MAGIC_LITERAL_IN_COMPARISON · All error codes

What went wrong

A literal value is used directly in a comparison, relational, or range expression. Bare literals at decision points carry no semantic signal - the reader must infer what the value means from surrounding context.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 317 (How does EK9 detect magic literals?)
  • ek9 -q 638 (Why should I use named constants instead of literal value...)
  • ek9 -q 320 (Why does EK9 report on readability?)

Rationale

About one change in six to duplicated code introduces a bug (CQSE, "Preventing Inconsistency Bugs", drawing on Juergens et al., ICSE 2009). The partial-update problem - changing N-1 of N occurrences - is exactly how such an inconsistency bug is made.

Anquetil et al. (2022) had 26 developers classify about 24,000 literals and only about a quarter were judged "magic" - context decides, which is why EK9 reports a bare literal only where it decides something: in a comparison, a relation or a range.

For AI code analysis, a named value is immediately understood. A bare literal requires cross-referencing against context - expensive inference that a name eliminates entirely.

How to fix it

Give this literal a meaningful name, where it is written.

With tooling, ek9_extract_constant (file, the literal as written, a name) makes it a module constant and rewrites the use in one call - but only for a literal already in the file. If a tool edit was rolled back for this error, nothing was written and the literal is not in the file: define the constant first with ek9_new_construct mode:'constant' (a name, the literal as its value), or declare a local in the same edit, and use the name in the edit you resend.

By hand, choose the right scope:

  • Option 1 - a local variable, for a value specific to this function (good_local.ek9 in the [EXAMPLE] section below)
  • Option 2 - a named constant, for a shared domain concept (good.ek9 below: an ADULT_AGE constant compared with an age)

Use a local variable when the value is a local algorithmic concern. Use a constant when the value represents a shared domain concept that other functions might also need, or that a maintainer would need to find and change in one place.

[STRUCTURAL CONSTRAINT INSIDE RETURN-DECLARED FUNCTIONS] When the enclosing function has a returning variable, local variables CANNOT appear between the incoming parameters ('->') and the returning variable ('<-'). The function body has a strict order: the parameters, then the returning variable, then the statements (including any local variables used below). A local threshold written between the two arrows is a PARSE ERROR, not a fix.

Inside a return-declared function you have two working options:

  • Option A - declare the returning variable without a value, declare the local after it, then give the returning variable its value (good_local.ek9):
isExpensive() as pure
  -> price as Float
  <- rtn as Boolean?
  //A local goes after the returning variable, never between '->' and '<-'
  expensiveThreshold <- 50.0
  rtn :=? price > expensiveThreshold
  • Option B - extract the threshold to a module constant (preferred when the value is a domain concept): the returning variable is then initialised directly from a comparison with the constant, as good.ek9 does.

Option B is usually cleaner for thresholds in pure functions - no local, no later assignment, one obvious place to change the value.

See also

  • E11065: repeated literal - extract to named constant
  • E11061: 2+ Boolean literals require named arguments

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E11064: literal in comparison/range - give it a meaningful name

#!ek9
defines module error.e11064.bad

  defines function

    isAdult()
      -> age as Integer
      <- rtn as Boolean: age >= 18

Fixed: Give the literal a name

#!ek9
defines module error.e11064.good

  defines constant

    ADULT_AGE <- 18

  defines function

    isAdult()
      -> age as Integer
      <- rtn as Boolean: age >= ADULT_AGE

Fixed: A local variable names a value specific to one function - declared after the returning variable

#!ek9
defines module error.e11064.good.local

  defines function

    isExpensive() as pure
      -> price as Float
      <- rtn as Boolean?
      //A local goes after the returning variable, never between '->' and '<-'
      expensiveThreshold <- 50.0
      rtn :=? price > expensiveThreshold

Questions and answers

These Q&A entries show E11064 and its fix: