E11010: excessive complexity - refactor
What went wrong
Your function/method exceeds the cyclomatic complexity threshold of 45. Cyclomatic complexity measures independent paths through code. Each control flow statement (if/while/for/switch/try) adds a path.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1020 (My function is too complex and the compiler rejects it...)
- ek9 -q 1020 (My function is too complex and the compiler rejects it...)
- ek9 -q 1020 (My function is too complex and the compiler rejects it...)
How to fix it
- Extract helper methods for each logical branch
- Use guards to reduce nesting
- Replace nested if/else with switch when appropriate
- Break up complex coalescing expressions into intermediate variables
Expression complexity
- Coalescing operators (?:, <?, >?) add complexity
- Extract complex coalescing expressions to separate functions
Common causes
- Too many control flow statements
- Complex expressions with coalescing operators
- Multiple exit points and error handling paths
See also
https://ek9.io/quality.html#complexity https://ek9.io/forAI.json#$.qualityMetrics.metrics[?(@.errorCode=="E11010")]
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E11010: excessive complexity - refactor
#!ek9 defines module error.e11010.bad defines function isPositive() as pure -> item as Integer <- rtn as Boolean: item > 0 isLarge() as pure -> item as Integer <- rtn as Boolean? threshold <- 10 rtn: item > threshold doubleIt() as pure -> item as Integer <- rtn as Integer: item * 2 addTen() as pure -> item as Integer <- rtn as Integer: item + 10 joiner() as pure -> a as Integer b as Integer <- rtn as Integer: a + b defines function <?- Multiple complex pipelines with type-changing operations push the function complexity over the EXCESSIVE_COMPLEXITY threshold of 45. Pipeline 1: sort(1) + group(2) + flatten(2) + map(2) + tee(2) + filter(1) + head(1) = 11 Pipeline 2: filter(1) + map(2) + sort(1) + map(2) + skip(1) + head(1) = 8 Pipeline 3: sort(1) + filter(1) + map(2) + map(2) + tee(2) + uniq(1) = 9 Pipeline 4: filter(1) + sort(1) + join(2) = 4 Sources: 4 x cat(1) = 4, terminals: 4 x (1) = 4 Other: exit(1) + require(3) + if(2) + comparisons(4) = 10 Total: 11 + 8 + 9 + 4 + 4 + 4 + 10 = 50 -?> BadStreamComplexity() stdout <- Stdout() sideCapture1 <- List() of Integer sideCapture2 <- List() of Integer //Pipeline 1: sort, group, flatten, map, tee, filter, head (7 stages, 11 weighted) cat [8, 3, 5, 3, 8, 1, 5, 2, 1, 8] | sort | group | flatten | map by doubleIt | tee in sideCapture1 | filter by isPositive | head 3 > stdout //Pipeline 2: filter, map, sort, map, skip, head (6 stages, 8 weighted) cat [100, -50, 200, -75, 300, 25, -10, 150] | filter by isPositive | map by doubleIt | sort | map by addTen | skip 1 | head 3 > stdout //Pipeline 3: sort, filter, map, map, tee, uniq (6 stages, 9 weighted) cat [5, 10, 15, 5, 20, 10, 25, 15, 30] | sort | filter by isLarge | map by doubleIt | map by addTen | tee in sideCapture2 | uniq > stdout //Pipeline 4: filter, sort, join (3 stages, 4 weighted) total <- cat [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | filter by isPositive | sort | join with joiner | collect as Integer //Pipeline 5: sort, group, flatten, map, filter, head (6 stages, 10 weighted) cat [7, 2, 9, 2, 7, 4, 9, 1] | sort | group | flatten | map by addTen | filter by isLarge | head 5 > stdout //Pipeline 6: filter, map, sort, map, uniq (5 stages, 7 weighted) cat [11, 22, 33, 11, 44, 22, 55] | filter by isPositive | map by doubleIt | sort | map by addTen | uniq > stdout //Pipeline 7: sort, group, flatten, map, tee, filter, map, uniq, head (9 stages, 16 weighted) cat [3, 6, 9, 3, 12, 6, 15, 9, 18] | sort | group | flatten | map by doubleIt | tee in sideCapture1 | filter by isPositive | map by addTen | uniq | head 5 > stdout require total? require length sideCapture1 > 0 require length sideCapture2 > 0 if total > 0 stdout.println($total) minCapture <- 2 if length sideCapture1 > minCapture stdout.println("captured enough")
Fixed: Give each pipeline its own function
#!ek9 defines module error.e11010.good defines function isPositive() as pure -> item as Integer <- rtn as Boolean: item > 0 doubleIt() as pure -> item as Integer <- rtn as Integer: item * 2 addTen() as pure -> item as Integer <- rtn as Integer: item + 10 firstThreeDoubled() -> numbers as List of Integer <- rtn as List of Integer: List() rtn: cat numbers | sort | uniq | map by doubleIt | filter by isPositive | head 3 | collect as List of Integer shiftedPositives() -> numbers as List of Integer <- rtn as List of Integer: List() rtn: cat numbers | filter by isPositive | map by addTen | sort | collect as List of Integer defines program StreamsInParts() stdout <- Stdout() stdout.println($firstThreeDoubled([8, 3, 5, 3, 8, 1])) stdout.println($shiftedPositives([100, -50, 200]))