Hello. I think a function List.walkBackwardsFromUntil would make sense (I searched the Roc documentation and didn't find such a function). The function, if implemented, would walk a list backwards but in addition to what List.walkBackwardsUntil offers, the function would be given an index to specify where to start the walk from. Any opinions?
Hi @Petri Jääskeläinen, my first impression is that does feel like a very specific scenario. We plan on implementing highly efficient slices of lists. Taking a slice first and then doing walkBackwardsUntil would be my preferred approach.
We could also consider a walk function which accepts a List.range style record as its first argument, allowing more cases (including configurable backwards, forwards, and stepped walking).
While this generally wouldn't be meaningfully more efficient than walking a slice of the original list, in the case where someone wants to walk on a step other than 1, this would be more efficient/convenient, unless we supported lists with a strided view on the backing data (which could be useful for directly supporting multi-dimensional lists and zero-cost reshaping, but would be somewhat less efficient for the single-dimension contiguous list case)
Interesting, I would keep the current walk and walkBackwards to not make common cases tedious but I can see value in adding another more configurable function like walkSpecific or something.
Or make some more of record fields optional in this case compared to range, since the endpoints can be inferred (as well as some shorthand fields)? iow walk list {} \... is equivalent to a present forwards walk, and walk list {dir: Backwards} \... is equivalent to a present walkBackwards
dir: Rev (paired with Fwd) might be a convenient abbreviation
I still would keep the current walk as is, but for another function I'd like optional record fields.
I like the idea of having a configurable walk!
regarding naming, here's an interesting idea: what if we did this?
walk to foldwalk which takes a configuration record of lots of optionsso basically walk becomes the most flexible configurable one, and fold becomes a shorthand for a common case: you want to walk forwards, one element at a time, starting from index 0, and never stop early
so for example it could become:
List.walk :
List elem,
{
start : state,
step : (state, elem -> state),
until ? (state -> Bool),
direction ? [Forward, Backward],
startIndex ? Nat
}
-> state
I think you can implement all the existing functions using this (e.g. every combination of "backwards", "until", "from", "withIndex") and I suspect LLVM would optimize them down to the same thing too
I actually originally wanted to have a function like this that takes a record for learning purposes, e.g. I suspect that, for a beginner, having the labels here might be helpful:
List.walk numbers { start: 0, step: \num -> num + 1 }
step's naming could perhaps be clarified... each perhaps?
"walk" and "step" seem related concepts?
I just mean that step in range refers to the counting/selection interval, and in Richard's proposal it refers to the function that is called for each elem
A range-style step concept could be applied here, as well as start and end, to just walk some of the elements. While slicing could take care of the range-style start and end concepts, slicing could not take care of the step concept, and in any case, I believe those could all be conveniences we could add, thus we should reserve the range-style naming for the identical range-style comments.
From the Advent of Code problems, there were a couple times I used range and walk (and get and Result.withDefault), or sublist and walk together just because walk could not itself do what I needed
Thus for the record fields, I'd propose:
At 0 and Before (List.len list){start: Last, end: First} would be equivalent to Backwards ?direction could get added to List.range if present in List.walk.state or initial would be replacement naming for the start field from Richard's proposal.apply would take the place of both step and until from Richard's proposal, and have the type: (state, Nat) -> [Stop state, More state] -- the Nat would be the index, since alongside start, step, etc, that's tedious and error prone math to make the programmer deal with via state.Break and Continue could be familiar naming instead of Stop/More.I think we can omit direction and just follow list.range. use start, end, and step to define the direction.
Also, i am not sure about the more complex function by default. It is wanted in some cases, but forcing use of break and continue for simple function may be verbose and a tad confusing for beginners.
I think splitting into more pieces so that a user can learn it one record field at a time may be more learnable
I like the idea of First and Last as defaults.
Last updated: Jun 16 2026 at 16:19 UTC