#maintaining internal state with list.fold or list.scan

1 messages · Page 1 of 1 (latest)

ripe nexus
#

possible I'm not doing this the best way (maybe it's better to write functions that do recursion instead since that's what fold/scan do internally)

but I've often have some internal state I want to maintain, but also want to return something externally. having to pack these into tuples seems annoying

#

for example, this rather contrived example

import gleam/list
import gleam/io
import gleam/pair

pub fn main() {
  list.range(1, 100)
  |> list.shuffle
  |> list.scan(#(0, False), fn(acc, num) {
    let #(prev, _) = acc
    case prev - num > 0 {
      True -> #(num, True)
      False -> #(num, False)
    }
  })
  |> list.map(pair.second)
  |> io.debug
}
#

(along a similar line, something that's possible in python, i.e.

for (a, b) in [(1, 2), (3, 4)]:
  # do stuff

being able to do that in functions would also be helpful?

fn(#(prev, _), num) {}

since that at least removes the need for the destructuring

stone crater
#

This is brought up from time to time, here is one discussion: https://github.com/gleam-lang/gleam/discussions/1451

My coworker is doing AoC in Gleam this year and also mentioned they would like to see this allowed

My main worry is that it would be confusing (e.g. to beginners) why only tuple pattern is allowed in a function head. Possibly this would also add a little bit of complexity to the compiler codebase? Not sure, maybe, e.g. around labeled (and soon default) arguments, documentation generation, maybe somewhere in the LSP as well.

steel moth
#

I also find myself often destructuring a pair of values, let #(idx, val) = idx_val or when I need to return a couple of different things when parsing the same input for AoC. For larger output values I create a separate type type Acc { Acc(...) } which is used only once.

ripe nexus
stone crater
#

I'd also use a tuple or a custom state type for this, don't know of a better way

ripe nexus
#

I think you can relatively trivially modify list.scan:

pub fn scan_state(
  over list: List(a),
  from initial: acc,
  state: state,
  with fun: fn(acc, state, a) -> #(acc, state),
) -> List(acc) {
  do_scan(list, initial, [], state, fun)
}

fn do_scan(
  list: List(a),
  accumulator: acc,
  accumulated: List(acc),
  state: state,
  fun: fn(acc, state, a) -> #(acc, state),
) -> List(acc) {
  case list {
    [] -> reverse(accumulated)
    [x, ..xs] -> {
      let #(next, next_state) = fun(accumulator, state, x)
      do_scan(xs, next, [next, ..accumulated], next_state, fun)
    }
  }
}

but my thoughts at that point is why not just write your own recurisve solution rather than writing a function to do it

stone crater
#

All seem valid! I would probably use fold instead of scan, having an accumulator be a tuple of a List(Bool) and Int

stone crater
ripe nexus
#

as in you inline the function essentially

ripe nexus