Zulip Chat Archive

Stream: std4

Topic: Setting elements of arrays with GetElem


Frédéric Dupuis (Dec 08 2023 at 03:31):

Do we have something like this?

def natSet (as : Array α) (i : Nat) (v : α) (hi : i < as.size := by get_elem_tactic) : Array α :=
  Array.set as i, hi v

As far as I can tell, we only have Array.set which takes in a Fin index, Array.setD which does nothing if the index is out of bound, and Array.set! which panics.

Joachim Breitner (Dec 08 2023 at 08:47):

While we are at it, would it be good to have notation like

do
  a[i] := x

that desugars to

do
  a := a.natSet i x

?

Mac Malone (Dec 08 2023 at 08:53):

@Joachim Breitner See lean4#905 for an old discussion of better do notation assignment syntax. :grinning_face_with_smiling_eyes:

Kyle Miller (Dec 08 2023 at 16:31):

Patrick and I experimented earlier this year with a[i] := x notation. We did set a[i] := x since that made it be unambiguous (the normal x := v parser parses any term x for the LHS and interprets it as a pattern -- maybe we could have carved out indexing notation, but I don't remember if we tried).

In action it looks like

def matrix.swapRowsp (A : matrix m n) (row row' : )
    (hrow : row < m := by set_elem_tac) (hrow' : row' < m := by set_elem_tac) :
    matrix m n := Id.run do
  if row == row' then return A
  let mut B := A
  for h : j in [0:n] do
    let tmp := B[row, j]
    set B[row, j] := B[row', j]
    set B[row', j] := tmp
  return B

(set_elem_tac is like get_elem_tac, and I don't remember if it's any different. These were extended to deal with tuple indexing though.)

code

Kyle Miller (Dec 08 2023 at 16:32):

It's nice having array indexing have a symmetry before and after the := for code like this.

Kyle Miller (Dec 08 2023 at 16:35):

More generally, it's nice when a language has a concept of lvalues, which are expressions that come with assignment semantics. Common Lisp has a cool system of composable "setf macros", where in (setf p v) you explain for a given form p how to do the assignment v -> p.

Using Lean notation, that could mean macros that turn set s.x[1] := v into s := {s with x := s.x.set 1 v}. The macros could be written to be more careful about affine usage of the variables than this.

Kyle Miller (Dec 08 2023 at 16:37):

(In fact, setf macros were what justified prefixing these assignments with set for me. It also serves as a warning that there might possibly be non-affine uses of variables.)

Sebastian Ullrich (Dec 08 2023 at 16:51):

Lean already has lvalues, they just haven't made it to do yet

#check fun (p : Prod (Prod (Array Nat) Unit) Unit) => { p with fst.fst[0] := 1 }

Kyle Miller (Dec 08 2023 at 21:32):

If we had setf-style lvalues, then we could do something more general than what's available to structure notation, or at least I think -- I don't know if what you can do with structures is extensible.

The idea of setf is to make accessing and storing look the same, no matter the accessor function. For example, if you have a hash map m, you can do m.find! k to access, so you should be able to define a set macro so that set m.find! k := v means m := m.insert k v. (Justification for using find!: after this, m.find! k evaluates to v.)

I believe the lenses were invented for this sort of transformation, but we're in Lean and we have metaprogramming :-)

Kyle Miller (Dec 08 2023 at 21:37):

Common Lisp Hyperspec: setf and kinds of places


Last updated: Dec 20 2023 at 11:08 UTC