E11065: literal appears multiple times - extract to a named constant
What went wrong
The same literal value appears 3 or more times in this file (or 4+ times across files in the same module). Repeated literals are a maintenance liability - when the value needs to change, every occurrence must be found and updated. Missing one creates a silent bug.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 800 (Why does EK9 reject repeated literal values?)
Rationale
Juergens et al. ("Do Code Clones Matter?", ICSE 2009) found inconsistent changes to duplicated code very frequent - only some copies get updated - and every second unintentional inconsistency a fault.
SonarQube (S1192), PMD, and Checkstyle all flag repeated string/numeric literals at thresholds of 2-4 occurrences. EK9 uses 3 per file and 4 per module - balanced thresholds that minimise false positives while catching real duplication.
For AI, every occurrence of the same literal is a redundant token that must be mentally unified. A named constant is one concept, stated once, referenced everywhere - dramatically reducing the token cost of understanding the module.
How to fix it
If the repeats are ARGUMENTS of a call - a constructor or a function - label the arguments instead: Cookie(name: "session", cookieValue: "abc123"). The label says what the value is, the compiler checks it against the declared parameter (E06250), and a labelled argument is not counted. Label every argument of the call (all or none).
Otherwise - and whenever the value is genuinely shared, such as the same locale tag in several files - extract the repeated literal to a named constant.
With tooling, ek9_extract_constant does it in one call: file, the literal as written (literal:'"java/lang/StringBuilder"') and a name - every occurrence already in the file is replaced and the `defines constant` block is created or extended for you. If a tool edit that added another copy was rolled back for this error, that copy was never written: run ek9_extract_constant on the copies already in the file, then use the name in the edit you resend - or, when none is in the file yet, define the constant first with ek9_new_construct mode:'constant' (a name, the literal as its value). Do not hand-insert a `defines constant` block with a text edit: the block's position and indentation are the compiler's rules, and a misplaced one cascades (measured: 15 new diagnostics from one raw edit).
By hand: declare the value once as a named constant in a constant block of the module and use the name everywhere the literal was. The [EXAMPLE] section below shows "processing" printed three times (and the same literal repeated in constructor and function calls), then a TAX_RATE constant used by three functions.
The constant creates a single point of change and gives the value a name that communicates its purpose.
See also
- E11064: magic literal in comparison
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E11065: literal appears multiple times - extract to a named constant
#!ek9 defines module error.e11065.bad defines function testRepeatedString() stdout <- Stdout() stdout.println("processing") stdout.println("processing") stdout.println("processing") //A constructor call is not exempt (D4): it was once, by the callee's name starting with an uppercase //letter. Label the arguments - Cookie(name: "session", cookieValue: ...) - or name a shared value. testRepeatedStringInConstructors() c1 <- Cookie("session", "abc123") c2 <- Cookie("session", "def456") c3 <- Cookie("session", "ghi789") require c1? and c2? and c3? //The same heuristic exempted any FUNCTION whose name starts with an uppercase letter. testRepeatedStringToUppercaseFunction() first <- Shout("hello") second <- Shout("hello") third <- Shout("hello") require first? and second? and third? Shout() -> text as String <- rtn as String: text.upperCase()
Fixed: Name the literal once, as a constant
#!ek9 defines module error.e11065.good defines constant TAX_RATE <- 0.2 defines function tax() -> amount as Float <- rtn as Float: amount * TAX_RATE net() -> amount as Float <- rtn as Float: amount - amount * TAX_RATE gross() -> amount as Float <- rtn as Float: amount + amount * TAX_RATE
Questions and answers
These Q&A entries show E11065 and its fix: