Skip to content

Flow.range with a negative step emits only the first element #439

@haskiindahouse

Description

@haskiindahouse

Flow.range(5, 1, -1) emits [5] instead of [5, 4, 3, 2, 1]. The continuation condition only handles ascending ranges.

Reproducer (scala-cli):

//> using scala 3.8.3
//> using dep com.softwaremill.ox::core:1.0.4

import ox.flow.Flow

@main def repro(): Unit =
  println(Flow.range(5, 1, -1).runToList())   // List(5)            — expected List(5, 4, 3, 2, 1)
  println(Flow.range(10, 0, -2).runToList())  // List(10)           — expected List(10, 8, 6, 4, 2, 0)

Source:

https://github.com/softwaremill/ox/blob/770bed97a247a2536832ff7485953d75f53ccbeb/core/src/main/scala/ox/flow/FlowCompanionOps.scala

def range(from: Int, to: Int, step: Int): Flow[Int] = usingEmitInline: emit =>
  var t = from
  repeatWhile:
    emit(t)
    t = t + step
    t <= to                       // wrong direction when step < 0

For descending ranges (step < 0), the loop guard should be t >= to. Scala's own 5 to 1 by -1 follows that convention.

There's also a corner case in the other direction: Flow.range(1, 5, -1) would loop until Int underflow, since t <= to stays true. Probably worth a step != 0 precondition while in the area.

Suggested fix:

def range(from: Int, to: Int, step: Int): Flow[Int] =
  require(step != 0, "step must be non-zero")
  usingEmitInline: emit =>
    var t = from
    repeatWhile:
      emit(t)
      t = t + step
      if step > 0 then t <= to else t >= to

Happy to PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions