AW: Duplicate keys in map construction

Hi Michael,

> Unfortunately this cannot be implemented using map:put as a primitive, because `map:put($map, $key, $value)` is currently defined to return a map in which the new `$key` replaces the old.

My feeling is that the current definitions are a bit ambiguous or vague. XQFO 4.0 says for map:put:

> The entry order of the entries in the returned map is as follows: if $map contains an entry whose key is $key, then the new value replaces the old value and the position of the entry is not changed; otherwise, the new entry is added after all existing entries.

The last sentence implies that only the value is replaced.

XPDM says:

> The key/value pairs in the returned map are as follows:
> • One key/value pair for every key/value pair present in $map whose key is not equal to $key; plus
> • One key/value pair whose key is $key and whose associated value is $value.
> The entry order in the returned map reflects the entry order in the supplied $map. If the key of the new entry was present in $map then the new entry replaces that entry retaining its current position; otherwise, the new entry is added after all existing entries.

It implies that there will be a new entry (consisting of the supplied key and value) which will replace the old one. Maybe we can refine it: "If the key of the new entry was present in $map" implies that the key is exactly the same key, but we actually have 3 cases:

1. Keys are exactly the same (e.g., integers 1 and 1)
2. Keys are regarded as equal, but are different (e.g., integer 1 and double 1e0)
3. Keys are different (e.g., integers 1 and 2)

I believe that 2. can be restricted to cases for which fn:atomic-equal returns true, but where the type of the compared keys differs. In https://github.com/qt4cg/qtspecs/issues/1725#issuecomment-2609624271, my proposal was to treat 2. and 3. identically:

> Another edge case that may need to be refined in the spec is the replacement of different keys that are deemed equal (1 vs 1.0) with map:put:
> { 1: 'integer', 2e0: 'double' } => map:put(1.0, 'decimal')
> As the stored key is changed by this update, I think we should treat it not as an in-place update, but as a delete-and-insert operation.
> One advantage of this would be that the resulting order would unveil what has been going on.

What do you think?

> I considered using map:replace() instead of map:put(). The prose of map:replace suggests that the old key is retained, but the "formal equivalent" is defined using map:put(), which uses the new key.

There is an open issue that may result in dropping map:replace. Maybe we could decide rather earlier than later whether we want to keep the function: https://github.com/qt4cg/qtspecs/issues/583.

Thanks,
Christian

Received on Wednesday, 29 January 2025 09:41:47 UTC