E07845: a for-range needs an ordered, steppable type - this type has no ordering and no step

Category: E07xxx Full resolution · Reported in phase: FULL_RESOLUTION · Classification: NOT_RANGEABLE · All error codes

What went wrong

A for-RANGE was written over a type that has no ordering and no step, so there is no sequence for the loop to walk. The common case is a function: 'for x in fn1 ... fn2'.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 64 (How do I write a for loop over a range of numbers?)
  • ek9 -q 1149 (Iterate from 0 to 20 with a step of 5 using for-range wit...)
  • ek9 -q 89 (How do I use streams and stream pipelines in EK9?)

What can be ranged

Any type providing an ordering ('<=>'), a step ('++'/'--') and a copy constructor (the loop makes its counter with one, E08272) - which includes YOUR OWN types, not just built-ins. The [EXAMPLE] below ranges over Integer and Character, and over a user type, Step.

So this error is not "only built-ins can be ranged". It means the type could never provide those operators. A user type that is simply MISSING one reports E07620 instead, because there adding it is the fix.

[WHY THIS IS NOT "ADD AN OPERATOR"] A for-range drives its counter with '<=>' and a copy constructor, plus '++'/'--' or '+=' when a 'by' step is given. A previous version of this check reported "operator '<=>' is required", which invites you to add that operator - but a function cannot declare operators at all, so no amount of adding fixes it. The shape simply is not meaningful: two functions do not describe a range, because there is no sequence of values between them.

[THIS IS NOT "FUNCTIONS CANNOT BE STREAMED"] Functions stream perfectly well - what cannot happen is ranging BETWEEN two of them. Hold them in a collection and the collection is what gets iterated (one of the fixes in the [EXAMPLE] below streams a List of functions).

How to fix it

  1. If you meant to process several functions, put them in a collection and 'cat' it, as above
  2. If you meant a numeric or ordered range, use a type that has an ordering and a step - Integer, Character, Date, Time, Duration and similar
  3. If you meant to call the functions, call them rather than ranging them

Common causes

  • Reaching for 'for ... in a ... b' when the intent was to iterate a collection, which is 'for item in collection' or 'cat collection'
  • Confusing 'a ... b' (a range between two values) with iterating a collection, which needs 'for item in collection' or 'cat collection'

See also

  • E07840: it does not have compatible iterator() - hasNext()/next() methods
  • E07620: operator not defined (where adding the operator IS the fix)

Examples

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

Fails with E07845: a for-range needs an ordered, steppable type - this type has no ordering and no step

#!ek9
defines module error.e07845.bad

  defines function

    low() as pure
      -> text as String
      <- rtn as String: text

    high() as pure
      -> text as String
      <- rtn as String: text

    printSteps()
      stdout <- Stdout()

      //A function has no order and no next value
      for step in low ... high
        stdout.println("step")

Fixed: Range over an ordered, steppable type

#!ek9
defines module error.e07845.good

  defines program

    PrintSteps()
      stdout <- Stdout()

      //Integer and Character are ordered and steppable
      for step in 1 ... 3
        stdout.println($step)

      for letter in 'a' ... 'c'
        stdout.println($letter)

Fixed: To process several functions, stream a collection of them

#!ek9
defines module error.e07845.good.stream

  defines function

    Shout() as abstract
      -> text as String
      <- rtn as String?

    loud() is Shout
      -> text as String
      <- rtn as String: text.upperCase()

    quiet() is Shout
      -> text as String
      <- rtn as String: text.lowerCase()

    applyIt()
      -> shout as Shout
      <- rtn as String: shout("Hello")

  defines program

    //To process several functions, stream a collection of them
    ShoutAll()
      stdout <- Stdout()
      handlers as List of Shout: [loud, quiet]
      cat handlers | map with applyIt > stdout

Fixed: Or a type of your own that is ordered and steppable

#!ek9
defines module error.e07845.good.user

  defines class

    //Ordered ('<=>'), steppable ('++' and '--'), and copyable (the copy constructor)
    Step
      position as Integer?

      default private Step() as pure

      Step() as pure
        -> initial as Integer
        position :=? Integer(initial)

      Step() as pure
        -> other as Step
        position :=? Integer(other.position)

      operator <=> as pure
        -> other as Step
        <- rtn as Integer: position <=> other.position

      operator #? as pure
        <- rtn as Integer: #?position

      operator $ as pure
        <- rtn as String: $position

      operator ++
        position++

      operator --
        position--

      override operator ? as pure
        <- rtn as Boolean: position?

  defines program

    PrintSteps()
      stdout <- Stdout()
      for step in Step(1) ... Step(3)
        stdout.println($step)