Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composition of incomplete mappings leads to weird behavior in complete #51

Open
dimitriye98 opened this issue Jun 4, 2021 · 0 comments

Comments

@dimitriye98
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant