metaeducation / ren-c

Library for embedding a Rebol interpreter into C codebases
GNU Lesser General Public License v3.0
128 stars 27 forks source link

Wish: Be able to HIJACK function with variant that adds refinements #1016

Open hostilefork opened 5 years ago

hostilefork commented 5 years ago

It would be neat to be able to say something along the lines of:

>> foo: func [x] [
    print ["x is" x]
]

>> foo-alias: :foo

>> hijack 'foo adapt (augment copy :foo [/negative]]) [
    if negative [x: negate x]
]

>> foo/negative 10
x is -10

>> foo-alias/negative 20
x is -20

Currently this causes asserts/crashes.

hostilefork commented 5 years ago

Supporting this would (probably?) require significant redesign.

The problem is the impacts on functions derived from the original foo. Even if you can do surgery on foo to update its interface (so it knows the refinements are there), there can be arbitrarily many ADAPTs, CHAINs, ENCLOSEs, SPECIALIZEs, which were built on the original notion of foo as having one argument. Invoking these derivations will build frames that are the wrong size. One would need backwards pointers from a function to every function derived from it to update the derivations--and this is not how things work at present.

...although, perhaps it would be possible for there to be a detection of when a frame is too small...and then be willing to expand it at runtime (with an assert presumably that there is a hijacking in effect to motivate the expansion). This wouldn't be particularly performant; and it would lead to issues where specialization frames would have to be checked to see if they were past the end in the evaluator loop.

Regardless, this is not a trivial feature. Though it would be desirable if it could be achieved.

Mark-hi commented 5 years ago

Interesting, yes. Desirable, maybe. Expected, perhaps not. Consider the following:

>> a: [1 2 3]
>> b: a
>> a: append copy a [4 5 6]
>> b
== [1 2 3]

Would anyone have the expectation that since b pointed to the same thing a points to, changing a copy of a would change b? Even though that is what would happen if the code had modified a directly, a fact which can trip up new users?

The code in this issue also copies, and hence should not work as stated.

That being said, foo-alias should work exactly like the unmodified foo, and it certainly should not assert or crash, so, if that is the issue, I will delete this comment -- assuming that everything works as stated if foo is used directly, and not a copy of it, by the adapt.

hostilefork commented 5 years ago

That being said, foo-alias should work exactly like the unmodified foo, and it certainly should not assert or crash, so, if that is the issue, I will delete this comment -- assuming that everything works as stated if foo is used directly, and not a copy of it, by the adapt.

Hm, you may be right here, the issue being that you can't hijack an adaptation without running into some amount of problems...see Ingo's issue here:

https://github.com/metaeducation/ren-c/issues/1012

If you don't COPY you cause a recursive loop. If you do COPY, you don't get the new behaviors.

Maybe it is the case that if you hijack with an expanded frame, only the function you hijacked gets the new refinements...and derivations are stuck unless you hijack them too?

hostilefork commented 4 years ago

A year down the road, there've been actual mechanical advancements in information hiding...firming up some of the model:

https://forum.rebol.info/t/advancements-in-information-hiding-sealed-frame-members/1396

With all that hammered out, I think it's safe to this feature is in the "not going to happen" territory. There are simply too many other more important features that hinge on having the identity of a paramlist be invariant.

Conceptually speaking: If you have access to the callsites enough to add refinements to them, you must have access to edit them. So...edit them to call the different function.

hostilefork commented 3 years ago

With all that hammered out, I think it's safe to this feature is in the "not going to happen" territory. There are simply too many other more important features that hinge on having the identity of a paramlist be invariant.

As it turns out...this is actually likely to be possible, due to rethinking how actions are structured (for other reasons, that just happen to enable this).

"parameter arrays" (ACT_PARAMLIST()) are no longer being conflated with function identity. Instead, function "detail arrays" (ACT_DETAILS()) are now serving as the identity, and pointing to the parameter arrays. This makes it easy to share parameter lists among function variations.

Since actions just point to parameter lists, a hijacked function pointing to a different parameter list than the one it had before doesn't cause structural disruption to functions that are derived from one another.


Hmmm...well, there's another technical problem... even though you can have a hijacking dispatcher do transformations to work around the case when a frame was built for an adaptation that wasn't quite compatible, once you un-hijack the function then any adaptations built while the function was hijacked will now depend on the modified paramlist. So basically this would eliminate the idea of ever un-hijacking a function and returning it to its previous performance characteristic...which is a nice feature of hijacking. Probably a nicer feature than adding refinements to a hijacking.

Though maybe it could be an option... HIJACK/SEVERE or something, which would commit to a permanently transforming dispatcher. Will continue to think on it.