Pitfalls when (ab)using events as objects

Can someone please explain the difference between:

(
var thing = (
	\sum : { 
		| self, a, b |
		a + b;
	},
	
	\mysum : { 
		| self, a, b |
		a + b;
	}
);

thing.sum(4,5).debug("sum");            /// -> returns 8
thing.mysum(4,5).debug("mysum");  /// -> returns 9

)

I’m assuming it has something to do with keys that have some predefined meaning (perhaps through the default event?) but my understanding remains very vague at this point.

The magic object-style referencing of the properties of an Event works by intercepting doesNotUnderstand calls, which happen when you call a method on an object that doesn’t exist. So, you call event.foo, and it has no .foo method, so doesNotUnderstand is called - this would normally throw an error, but in case of Event, it looks at the Event’s keys to see if there’s a similarly named item stored inside.
In the case you showed, this doesn’t work because Event already has a sum method (I think defined in Collection:sum?) - so this simply executes the method that was requested. However, mysum doesn’t exist, so it hits the doesNotUnderstand and in turn performs the override behavior.

Ah! I understand now how it works, thanks.

Would there be some way to get a warning when overwriting existing methods?

I think I found a way as follows

(
var thing = (
	\safeReg : {
		| self, name, implementation |
		if (self.respondsTo(name)) {
			
			("Error registering! Member" + name + "exists already").postln;
		} {
			if (self.keys.includes(name)) { 
				("Error! Cannot re-register" + name).postln;
			} {
				("Registered method" + name).postln;
				self[name] = implementation;
			};
		};
	};
);

thing.safeReg(\mysum, { | self, a, b | a+b; }); // should succeed
thing.safeReg(\sum, { | self, a, b | a+b; });   // should fail
thing.safeReg(\mysum, { | self, a, b | a+b; }); // should fail now as it overwrites previously registered thing

thing.mysum(4,5).debug("mysum");
)

You can also take a look at this Quark, which deals with the issue of base-class methods blocking “dynamic” lookup: https://github.com/telephon/Neutral

Actually a check like that is done in Environment already, as documented in the help page. Alas, the check/warning is only active when you use setters. If you initialize the whole event/environment, you don’t get that check/warning.

NOTE: Be careful to avoid method names that are defined in any of the superclasses of environment (or event). Object prototyping works by trapping method selectors that are not already defined as class library methods. Using a generic method selector such as ‘stop’ or ‘reset’ will cause the corresponding class library method to respond, and the items in the environment will never be checked.
Assigning a value into an environment using a setter – name_() or .name = … – posts a warning message if the name is already defined in the class library.

e.reset = { "My reset function".postln };
​
// prints:
WARNING:
'reset' exists a method name, so you can't use it as pseudo-method.
​
// this does NOT execute the reset function above
// because Object:reset responds
e.reset;

I suspect it’s not hard to fix/improve the constructor of Environment and make it iterate over the values and find those that are functions and issue a warning there too. But perhaps it’s undesirable in some ways (e.g. performance)… Also, I suspect there are other code paths on which that check isn’t done (putAll, putPairs etc.)

Also of some interest perhaps, you can still access such functions that conflict with method names, just not as methods.

(sum: { 42 })[\sum].value // 42

This is probably why the warning is not universally given, but only when you use setters. The constructor is arguably a debatable case whether to give that warning or not…

Of some interest, James’ Proto inherits directly from Object so more stuff works overridden, including sum, but not stuff defined in Object like blend

a = Proto({~sum = 42})
a.sum // 42

a = Proto({~blend = 77})
a.blend // nil

Proto actually has wrappers for a bunch of methods defined in Object… but not quite everything.

@scztt: how does that work exactly just as a Quark (i.e. no C++ code changes)? Does stuff put in BaseClasses not inherit from Object?

It doesn’t attempt to solve that problem. It seems to be a proposal for a future design.

hjh