Requires elegant solution

/// ...

{ // create a function
	
	ObjectTable.put // access specific value
	
	(
		UniqueID.id // return integer key 
		
		, 
		
		(
			ObjectTable at: // time of definition...
			
			(
				UniqueID.id // return value changes...
			)
		)
		
		// ...
		
		// ...
	
	) 	// ...
}

// solution is unelegant 

// though works perfectly... 

.def

.sourceCode

.replace

(
	"UniqueID.id" , UniqueID.id
)

.interpret

// ...

The intent is to capture the integer value of UniqueID.id at the time of definition

At the time of evaluation, UniqueID.id will return a value from the future

The intent is to swiftly & gracefully access the object directly at the time of definition

I think what you’re looking for is called a function closure.

(
var f = { |id|
  {
	 id.postln;
  }
};
var g = f.value(UniqueID.id);

g.value;
g.value;
g.value;
)

In my example, f is a function that returns a new function. I call it with the value of UniqueId.id once, which means that g is now a function which you can call it as many times as you like and will always refer to that same value of id.

The caveat is every time a new element is added to ObjectTable, it’s assigned a new integer id, which is equivalent to UniqueID.nextID

UniqueID.id // default starts at 1000
// -> 1000
ObjectTable add: [ ] // returns id 
// -> 1001
UniqueID.id // 1001
// -> 1001

So far, the solution to ‘freeze’ the object has been:


{
     // function where we need the value of the id 

    // at the time of definition...

   // and *not* at the time of evaluation

}

.def

.sourceCode

.replace

(
	"UniqueID.id" , UniqueID.id
)

.interpret

I may be missing something obvious… suggestions?

Understand, though, that in jxa’s suggestion, the |id| argument to the function is local.

You pass in a specific ID, and the outer function returns a function that is bound to the scope containing the local variable.

After that point, it doesn’t matter what happens to UniqueID, because the function that was returned will always refer to the local variable.

To extend that example up to ObjectTable:

(
~withThing = { |thing|
	var id = ObjectTable.add(thing);
	{
		"inside this function, thing = % and id = %\n".postf(thing, id)
	}
}
)

f = ~withThing.([1, 2, 3]);

f.value;
inside this function, thing = [ 1, 2, 3 ] and id = 1001

g = ~withThing.([4, 5, 6]);

g.value;
inside this function, thing = [ 4, 5, 6 ] and id = 1002

Now, at this point, what do you suppose happens if you f.value again?

UniqueID’s state has changed, so you might be afraid that it would act on the wrong ID.

But…

f.value;
inside this function, thing = [ 1, 2, 3 ] and id = 1001

(Admittedly, though, f and g are now open functions, and as such, they don’t have a compile-string. This might be a problem for some scenarios, but I expect there would be a better solution than to interpret a string.)

hjh

Depending on your needs, a new class definition could help you with this too:

ObjectTableObject {
	var objectTableId;

	*new {
		^super.new.init;
	}

	init {
		objectTableId = ObjectTable.add(this);
		^this;
	}

	yourLogic {
		// can now refer to objectTableId here
	}
}

However, I think it’s worth asking yourself if you can implement your feature in such a way that the function doesn’t need to know so much about its context. It’s a sort of code-smell that your code needs to know it’s a part of an ObjectTable and how to address itself. Can you decompose the problem in such a way that those are separate concerns?

The intent is to capture the integer value of UniqueID.id at the time of definition

It’s not clear the UniqueID.id accessor method is particularly useful?

I wonder why it’s there.

If it weren’t the ObjectTable add class method would be:

	*add { arg obj;
		var k = UniqueID.next;
		global.put(k, obj);
		^k
	}

Valid question. UniqueID could be considered either a stream, or an allocator. Neither streams nor allocators let you ask “what was the last value?” – only to request the next one. It’s not clear to me why UniqueID adds id to this.

I had always assumed it was my responsibility to grab the next ID into my own variable (so, the original post might be mis-framing the question).

hjh

That’s what I assume too.

Either you generate and store the id yourself or you let ObjectTable generate it then you look it up.

~obj = (id: UniqueID.next);
ObjectTable.put(~obj.id, ~obj);
~obj.id == ObjectTable.getID(~obj);
~obj2 = ();
~table = ObjectTable.new;
~table.add(~obj2);
~obj2[\id] = ~table.getID(~obj2);

Thanks for all the replies everyone… I was truly having difficulty with this.

I believe the first response was in fact correct regarding “function closure”… this is a concept I haven’t fully grasped yet.

As for the replies regarding the relationship between UniqueID & ObjectTable, it’s rather interesting… they’re both inextricably related, while defined separately.

The following is the entirety of the source code which defines them both, and it should make a great deal of sense very quickly.

UniqueID {
	classvar <id=1000;
	*initClass { id = 1000; }
	*next  { ^id = id + 1; }
}

ObjectTable : TwoWayIdentityDictionary {
	classvar <global;


	*new {
		^super.new;
	}

	add { arg obj;
		this.put(UniqueID.next, obj);
	}

	*initClass {
		global = this.new;
	}
	*add { arg obj;
		global.add(obj);
		^UniqueID.id
	}
	*put { arg key, obj;
		global.put(key, obj);
	}
	*remove { arg obj;
		global.remove(obj);
	}
	*at { arg id;
		^global.at(id);
	}
	*getID { arg obj;
		^global.getID(obj);
	}
	*objPerform { arg id, selector ... args;
		var obj;
		obj = global.at(id);
		if (obj.notNil, {
			obj.performList(selector, args);
		});
	}
}