Yes that was more/later fancy idea that would keep a reference to the āold targetā event
/* "record override" not very useful
<> { arg anEvent;
^anEvent.copy.putAll(this)
}*/
<> { arg anEvent;
this.proto_(anEvent); ^this;
}
Both my proposals also happens to reverse the order composition just like next
in Event turns out to do compared with composeEvents
; see previous towards the end where I now realized (and edited this in). But to repeat that observation here:
(zz: 1).next((zz: 2)) // -> ( 'zz': 1 )
(zz: 1).composeEvents((zz: 2)) // -> ( 'zz': 2 )
(zz: 1) ++ (zz: 2) // -> ( 'zz': 2 )
And (later discovery) so that next
does not feel lonely, it also has a synonym: transformEvent
:
(zz: 1).transformEvent((zz: 2)) // -> ( 'zz': 1 )
And this one wants to be a slightly more general interface, alas itās documented on the page for nil
!
(\zz -> 1).transformEvent((zz: 2)) // -> ( 'zz': 1 )
(_[\zz] = 1).transformEvent((zz: 2)) // -> ( 'zz': 1 )
And of some interest, this one actually modifies the target argument event, so not actually identical to next
. The comment for in the code says
// Pattern support
transformEvent { arg event;
^event.putAll(this);
}
The association and function variants of this method also have this zero-copy semantics on the target event, i.e. they allow in-place modification.
But transformEvent
is not actually being called in the classlib, so I think it was deprecated in favor of next
, which is used in such (Pattern) contextsā¦
Obviously a <>
on Events should have the arguments order what next
does, not what ++
does because
(Pbind(\zz, 1) <> (zz: 2)).iter.next // -> ( 'zz': 1 )
For my own illumination, Iāve also tested these:
a = (zz: 1, od: 2)
b = (zz: 2, mo: 5)
// b gives defaults for a "with union" for uncommons
a.blend(b, 0) // -> ( 'zz': 1, 'od': 2, 'mo': 5 )
a.blend(b, 0) == a.next(b) // true
// b overrides a "with union" for uncommons
a.blend(b, 1) // -> ( 'zz': 2, 'od': 2, 'mo': 5 )
a.blend(b, 1) == (a ++ b) // true
// b gives defaults for a "with intersection";
// i.e. uncommons dropped "both sides"
a.blend(b, 0, false) // -> ( 'zz': 1 )
// b overrides a "with intersection" ...
a.blend(b, 1, false) // -> ( 'zz': 2 )
But it gets a bit more interesting, as the above are not all the ācombosā
// b overrides a on common keys,
// but a gets to keep just its own uncommons
// could be called "get news only if interested in topic(s)"
a ++ a.blend(b, 1, false) // -> ( 'od': 2, 'zz': 2 )
// the dual of the above, although I don't have a good name for it
// keep common stuff from a, uncommons from b
b ++ a.blend(b, 0, false) // -> ( 'mo': 5, 'zz': 1 )
There are also some boring combinations that yield either a
or b
back, e.g.
a ++ (a.blend(b, 0, false)) == a
a.next(a.blend(b, 0, false)) == a
I suppose I could look through the paper in the related discussion again and find the more CS names proposed for these, but I canāt be bothered right now. composeEvents
(and ++
) does work exactly like ārecord overrideā as defined in that paper.
Actually I did look even in the longer paper of Cardelli and Mitchell, but those donāt have names. given, although they are obtainable by a so-called restriction applied before override. The restriction removes keys not found in a given set.
From a more practical perspective, thereās nothing too deep going on in the above. There are 4 set ācombinationsā of keys (union, intersection, and the two original sets of keys) deciding which key set goes into the result, combined with another decision bit of āwho does the overrideā (or who āwins the conflictā) setting the values on the common keys. Of those 8 combinations in total, 6 are non-trivial but two give back the respective starting dicts. Alternatively explained, there a 3 orthogonal ādecision bitsā: whether to keep keys that exist only in a
, likewise for keys that exist only in b, and who gets to set the values on the common keys. (Thereās actually a 4th ābitā if one wants to consider not keeping common keys at all, but I havenāt considered that above, i.e. then there a 3 choices for the common stuff not two. And thatās making the decision āa prioryā just based on keys, not values. Bracha and Lindstrom actually defined their āmergeā to produce the common keys only if they agreed on values, but be undefined otherwise. Considering the values too makes the ādecision spaceā larger still.)
To refocus this discussion on SC though, I expect that very basic constraint on a <>
defined for Events to produce the same result as Pchain, at least for non-function fields, ie.
a = (foo: 2)
b = (foo: 1, bar: 0)
Pchain(a, b).iter.next == (foo: 2, bar: 0) == (a <> b)
which of course doesnāt happen at the moment because the rightmost expression is nil with the standard classlib.
Also
e = ()
p = Pbind()
e <> p // should be non-nil and return a Pchain(e, p) at least.