janet-lang / janet

A dynamic language and bytecode vm
https://janet-lang.org
MIT License
3.45k stars 223 forks source link

Wrong results when mutating table inside eachk / eachp #1220

Closed czkz closed 1 year ago

czkz commented 1 year ago
(let [t @{1 true 2 true 3 true 4 true}]
  (eachk k t
    (when (even? k)
      (put t k nil)))
  t)
# outputs:  @{1 true 3 true 4 true}
# expected: @{1 true 3 true}

Using (each k (keys t)) instead of (eachk k t) produces the correct result. Same applies to (each [k v] (pairs t)) instead of (eachp [k v] t).

sogaiu commented 1 year ago

I don't know what the intended behavior is, but FWIW, for forv there is this documentation:

    (forv i start stop & body)

    Do a C-style for-loop for side effects. The iteration variable `i` 
    can be mutated in the loop, unlike normal `for`. Returns nil.

By comparison, docs for for mention:

    (for i start stop & body)

    Do a C-style for-loop for side effects. Returns nil.

From these bits I had been thinking that without an explicit mention of "mutation is ok while iterating" that I wouldn't expect such behavior in other somewhat similar situations, but this is just speculation (^^;

Either way, perhaps some clarifying text in relevant docstrings might be nice.

primo-ppcg commented 1 year ago

The issue is the following:

(next @{1 true 3 true 4 true} 2)
# ->
nil

After 2 has been removed from the table, there is no "next key" after 2, and the iteration stops. If you need to mutate a table while iterating, it would be best to iterate over the keys, as you already mentioned.