-
-
Notifications
You must be signed in to change notification settings - Fork 7
Description
Not sure whether this is arguably more a Mercury issue, but all the code in question is in Lorenz, so posting it here.
If you have a mapping set ab
which exhaustively lists all mappings including overridden methods, and a mapping set bc
which is intended to chain from state b
but doesn't list overridden methods instead relying on complete
to infer them, the composition of the mappings, ab.merge(bc)
will never complete those methods, instead leaving them in their original state as in ab
.
For example, if we have class AImpl extends A
where both have method a()
and the mapping sets are as follows
ab:
A.a() -> B.b()
AImpl.a() -> BImpl.b()
bc:
B.b() -> C.c()
the result is
A.a() -> C.c()
AImpl.a() -> BImpl.b()
and completion will never properly complete the mapping.
Not sure what the solution here is. In my project using the library, I used a workaround where I modified complete to use put
instead of putIfAbsent
and basically treat the highest mapping in the class hierarchy as canonical, which worked for my purposes, but ideally you'd want the most complete mapping to dominate, regardless of where it is in the hierarchy.
While making more complete mappings "infectious" in a way which climbs up the hierarchy would require significant restructuring of the code, and may not really be necessary as it's a bit of an edge case, for incorporation into the upstream I think a solution which at very least will not override a more complete mapping with a less complete one is necessary. (Though, that's a tradeoff in and of itself, in that arguably it's not ideal for application of mappings to change the semantics of the code, and it may be preferable for a less complete mapping to override a more complete one rather than for application of the mappings to make a method no longer override a parent's method.)
The parenthesized concern aside, such a solution would more or less necessitate keeping track of the history of the mapping mergers for each mapping, and inheriting the parent mapping only if it's "longer" (i.e. more composition steps). It also raises questions regarding complicated topologies that can emerge when matching multiple mapping sets.
E.g. if we have classes class AImpl extends A
where both have method a()
, and the user merges ab.merge(bc).merge(ad)
resulting in a mapping set with both
A.a() -> B.b() -> C.c()
AImpl.a() -> DImpl.d()
Should A.a() -> C.c()
override AImpl.a() -> DImpl.d()
? That chain is longer, but on the other hand it doesn't actually continue the latter mapping.