Skip to content

Use of memory vars in map/set literals are subject to Clojure semantics (order not preserved except for small values) #97

@yuhan0

Description

@yuhan0

The interaction between Meander's "left-to-right, top-to-bottom" evaluation strategy and the unordered nature of Clojure's map and set literals could cause the following type of edge case:

(m/rewrite [1 2 3 4]
  [!n ...]
  {1 !n
   2 !n
   3 !n
   4 !n})
;; => {1 1, 2 2, 3 3, 4 4}

This appears to work fine until more than 8 elements are introduced, causing Clojure to switch from an ordered ArrayMap to unordered HashMap implementation.

(m/rewrite [1 2 3 4 5 6 7 8 9]
  [!n ...]
  {1 !n
   2 !n
   3 !n
   4 !n
   5 !n
   6 !n
   7 !n
   8 !n
   9 !n})
;; => {7 2, 1 6, 4 3, 6 9, 3 5, 2 1, 9 7, 5 8, 8 4}

@noprompt and @jimmyhmiller suggested using & syntax on RHS to guarantee evaluation order:

(m/rewrite [1 2 3 4 5 6 7 8 9]
  [!n ...]
  {& [[1 !n]
      [2 !n]
      [3 !n]
      [4 !n]
      [5 !n]
      [6 !n]
      [7 !n]
      [8 !n]
      [9 !n]]})

And similarly m/and on the LHS:
{1 !n ,,, 9!n} -> (m/and {1 !n} ,,, {9 !n})

I suggest introducing a check during compilation that would detect if the same memory-var is being referenced across more than one branch of a map or set literal, and then flag it as a warning/error due to the undefined semantics. (hopefully this does not prove too restrictive to cases where one does not care about order)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions