How is the undocumented feature "forward" in EnvironmentRedirect intended to be used?

In EnvironmentRedirect, I’m trying to understand what the original purpose of the undocumented feature forward is. So does anyone here know? The code that implements it is:

	know { ^envir.know }

	doesNotUnderstand { arg selector ... args;
		var func;
		if (this.know) {
			if (selector.isSetter) {
				selector = selector.asGetter;
				^this[selector] = args[0];
			};
			^this.doFunctionPerform(selector, args)

		};
		^this.superPerformList(\doesNotUnderstand, selector, args);
	}

	doFunctionPerform { arg selector, args;
		envir[\forward] !? {
			if(envir[selector].isNil) {
				^envir[\forward].functionPerformList(\value, this, selector, args);
			}
		};
		^this[selector].functionPerformList(\value, this, args);
	}

I’m planning to hack it to do something more a bit more ambitious, namely re-rewrap any of the original envir's returned values, including objects of the same class as (the currently wrapped) envir in an appropriate EnvironmentRedirect.

That will fix the issue with the way I’m using it that

e = EnvironmentRedirect(());
e.envir = (forward: e.envir); // undocumented feature
q = Quant.new(timingOffset: 0.1);
e.synchWithQuant(q); // returns an Event, so... in (ESP) code like
e = e.synchWithQuant(q); // wrapping/redirection is lost

Basically I change doFunctionPerform with something like

			if(envir[selector].isNil) {
				var retval = envir[\forward].functionPerformList(\value, this, selector, args);
				if(retval === envir, {^this}, {});
				if(retval.class === envir.class, {^this.copy.envir_(retval) }, {^retval});
			}

I may have to use a different name for my hack, if the present & unknown usage of forward somehow conflicts substantially with what I want it to do… So please advise.

JITLib / JITLibExtensions, the people who might know best – Julian Rohrhuber and Alberto de Campo – are more around the SC users / dev mailing lists, so maybe better to ask there.

BTW: you might want to check the mailing list archive, for former years this only works in a special way:
Go to https://www.listarc.bham.ac.uk/lists/sc-users-old/search/ and search there

1 Like

I see now that that \forward feature is actually present in the base IdentityDictionary:

	doesNotUnderstand { arg selector ... args;
		var func;
		if (know) {

			func = this[selector];
			if (func.notNil) {
				^func.functionPerformList(\value, this, args);
			};

			if (selector.isSetter) {
				selector = selector.asGetter;
				if(this.respondsTo(selector)) {
					warn(selector.asCompileString
						+ "exists as a method name, so you can't use it as a pseudo-method.")
				};
				^this[selector] = args[0];
			};
			func = this[\forward];
			if (func.notNil) {
				^func.functionPerformList(\value, this, selector, args);
			};
			^nil
		};
		^this.superPerformList(\doesNotUnderstand, selector, args);
	}

Although the documentation for ID’s know is fairly extensive, the \forward bit is not mentioned.

So the ERedirect was simply trying to make \forward still work across redirects.

In ID the \forward is basically a post-hook so that the user can add an “even more” customized (i.e. customized at runtime) handling of failed pseudo-method lookups.

(forward: { "Final fallback".postln }).adspfjh
// -> Final fallback