Wrapping my head around writing Classes

Hi!
I’m making a class.
This class needs a class method that when called embeds a ~variable
in a routine. This variable needs to be redefined when the routine yields,
e.g. if I redefine said variable with a new value wherever in the environment
I want to be able to return this new value with an instance method.

Now I got this:


RoutineEmbedder {
	var routine;
    *new { arg func;
		^super.new.init(func)
    }

    init { arg func;
		routine = r{
			func.embedInStream;
		}.loop
	}

	next {
		^routine.next;
	}
}

If I compile above class I can go:

b=Pseq([1, 2, 3, 4], 1);
a=RoutineEmbedder(b);
a.next;

To return values from the embedded pattern, but if I then do:

b=Pseq([0, 0, 0, 0], 1);
a.next;

It still keeps putting out the first pattern I embedded.

I’ve tried defining the patterns in {} and do .value.embedInStream in the instance method
but I think I’m missing some key part in how to construct classes. The concept of variables
In classes and how to set/get them is still sorta confusing to me.

If the answer I’m looking for is too involved for anyone to get into here you could just point me to a .sc that does something similar and i’ll figure it out myself.

cheers

Solved!

I just had to contain the argument in {}
like so :


RoutineEmbedder {
	var routine;
    *new { arg func;
		^super.new.init(func)
    }

    init { arg func;
		routine = r{
			func.value.embedInStream;
		}.loop
	}

	next {
		^routine.next;
	}
}

b=Pseq([1, 2, 3, 4], 1);
a=RoutineEmbedder({b});
a.next;

An analogy: You have a syringe full of medicine (from the doctor). You stick the needle in and push the plunger.

What happens to your body if you fill the syringe with different medicine?

Well, obviously, nothing – because there is no persistent connection between the syringe and your bloodstream.

a=RoutineEmbedder(b);b is the syringe. The Pseq is the medicine. You put the specific Pseq into the object.

There is no way to put b as a variable into the object. You can only put b’s contents in.

Therefore reassigning b does nothing to the object.

Here, do a.init(Pseq([0, 0, 0, 0], 1)).

b is irrelevant at that point.

hjh

Hmm, the thing is I want to be able to call a method on a without having to give it an argument and still have it refer to the ~variable declared in .new

I think I solved it though by doing:

    init { arg func;
		routine = r{
			func.value.embedInStream;
		}.loop
	}

And this:

a=Embedder({b})

Now I can do whatever with b and it works like intended. Or am i missing something?

Doesn’t declaring the argument as a function with a=Embedder({b}) do just that?

If the end goal is to change the embedder’s output stream, then we’re discussing a couple ways to do that:

  • The “classical mechanics cause and effect” way: change the embedder’s behavior by doing something to the embedder;

  • Or the “quantum mechanics spooky action at a distance” way: change the embedder’s behavior by changing a separate variable.

One of these is self-sufficient (to use a, you need only a), clear, and easy to troubleshoot; the other depends on potentially fragile relationships (to use a, you need a and b – this goes against the preference in object-oriented programming for clean encapsulation).

The “spooky action” way might seem cool and convenient, but experience tells me that you’ll start seeing bugs as things get more complex. To me, it looks like an anti-pattern and I would not recommend it. You’re free to try, of course, but you’re likely to run into trouble later.

This is not to mention that your “pass a function” solution might not be doing exactly what you thought:

b = Pseq([10, 11, 12], 1);
a = RoutineEmbedder({ b });

a.next;  // 10, OK

b = Pseq([0, 1, 2], 1);
a.next;  // 11 -- is that what you wanted?
a.next;  // 12 -- is that what you wanted?

a.next;  // 0 -- *now* it switches

The func.value happens only when embedInStream releases control, which happens only at the end of the pattern currently being embedded. But you had said that you wanted b=Pseq([0, 0, 0, 0], 1); a.next; to switch immediately. Your solution is not guaranteed to do that.

You can make the “classical mechanics” way look friendlier by defining a setter method instead of init:

// Class definition:
RoutineEmbedder {
	var <source, routine;

	*new { arg source;
		^super.new.source_(source)
	}

	source_ { arg argSource;
		source = argSource;
		routine = Routine { |inval|
			loop {
				inval = source.embedInStream(inval)
			}
		};
	}

	next { |inval|
		^routine.next(inval);
	}
}


// Usage:
a = RoutineEmbedder(Pseq([10, 11, 12], 1));

a.next;  // 10, OK

a.source = Pseq([0, 1, 2], 1);

a.next;  // 0, OK!

Also note the handling of inval – maybe you don’t need it yet, but you will, if you want to stream a Pbind.

Alternately, you could use an environment that broadcasts changes to registered client objects (the Observer OOP design pattern):

// Class definition:
RoutineEmbedder {
	var <source, routine;
	var <>key;

	*new { arg source;
		^super.new.source_(source)
	}

	source_ { arg argSource;
		source = argSource;
		routine = Routine { |inval|
			loop {
				inval = source.embedInStream(inval)
			}
		};
	}

	next { |inval|
		^routine.next(inval);
	}

	update { |obj, what ... args|
		// message would be:
		// envir.changed(\source, envVarName)
		// passing in obj = envir, what = \source, args = [envVarName]
		if(what == \source and: { args[0] == key }) {
			this.source = obj[args[0]];
		};
	}
}


// Usage:
(
e = EnvironmentRedirect(Environment.new)
.dispatch_({ |varname, value|
	e.changed(\source, varname)
});

a = RoutineEmbedder.new.key_(\b);

e.addDependant(a);
)

e.push;

~b = Pseq([10, 11, 12], 1);

a.next;  // 10, 11, 12...

~b = Pseq([0, 1, 2], 1);

a.next;  // 0, 1, 2...

// btw when you're finished with a:
e.removeDependant(a);

No – you haven’t passed b as a variable. You’ve passed a function that refers to b. If you were truly passing b as a variable, then the extra .value would be unnecessary.

hjh

When lost, (or in ?), I have found my dream solution method, drafting exact desired usage line by line, start to finish, & isolating x as the object of implement… just as one would isolate the variable in math or science.

// → with returns after comments

[x, y, z] for global variables
[k, v] for key → values
[x, n], [i, j], or [item, index] for iteration, deeper levels using: xx, nn, xxx, nnn, etc.

/*
explaining complex problems w/
1-3 line premise, such as:
"transform 2D array access from ~grid[y][x] to invert access ~grid[x][y]
*/

x = (36…99) // midi note #'s

x = x.reshape(8, 8) // 8x8, 64 pad matrix

x.printAll

x[0][0] // → 36 (bottom left corner)
x[7][7] // → 99 (top right corner)

x[0][1] // → 37 (one pad to the right of 36)
x[7][6] // → 98 (one pad to the left of 99)

// technically, this usage is ~grid.at(y).at(x), or ~grid[y][x]
// though we access first using the index of which ‘horizontal’ row,
// the first @ value, in this case, represents:
// “which row” or “which row, how high” or “which amount of vertical shift”…
// meaning: it is actually the ‘y’ value entered first, not the ‘x’
// implemented access is ~grid.at(y).at(x)
// ? is how to implement so one may use ~grid[x][y] or x[x][y]
// so that:

x[0][1] // ? → should be 44 and NOT 37
x[0][7] // ? → should be 92 and NOT 43

// so far…

x = x.collect{ |x, n| x = x.select{ |xx, nn| (nn % 8) === n } } // works, but too complex…

Does anyone know of a better way?

Hopefully this technique helps someone here… it mostly involves working backwards from how the solution should look… then exploring options and/or posting any further ?s to the forum list.

…and for those who must know:

example SOLUTION:

x = (36..99).reshape(8, 8).flop // → inverts rows and columns in an array-of-arrays

…and also

x = Array2D.fromArray(8, 8, x.flat) // → usage is now x[x, y]

Oh I was probably unclear in the original post.
This:

Is exactly what i want to have happen, hehe. Spooky action is good for me. Let me give some context as to what I’m doing with it:

~pattern1 = Pseq([1, 0, 0, 0, 0, 0, 0, 0],1);
~pattern1 = Pseq([1, 0, 0, 0, 1, 0, 0, 0],1);

~embedder = Embedder({~pattern1});


(
var array=0!8;
~arrayGen= {
	var shiftedArray, pat1, pat2, arrayOut;
	shiftedArray=array.shift(-1, 0);
	array=shiftedArray;
	pat1=~embedder.next;
	array.put(array.size-1, pat1);
	array;
};
)

16.do{~arrayGen.value.postln}

I used to do:
~embedder = r{~pattern1.embedInStream}.loop;
Which just looked ugly to me.

The ~arrayGen function might look unnecessary but i basically want the patterns and the ~arrayGen function to act separately of one another, e.g. I want to be able to rewrite and compile them freely while they are being played with like a Plazy without it messing up the timing of each individual item in the Pseq list.
In the above example for instance I want to be able to redefine the content of ~pattern when ever and still have the first item in the Pseq happen on the downbeat.
I also need to be able to do stuff like:

(
var array=0!9;
~hhAmpArrayGen= {
	var shiftedArray, pat1, pat2;
	shiftedArray=array.shift(-1, 0);
	array=shiftedArray;
	pat1=~embedder1.next;
	pat2=~embedder2.next;
    ((t.beats)%16).inclusivelyBetween(14,16)
	.if(
		{array.put(array.size-1, pat2)},
		{array.put(array.size-1, pat1)}
	);
	array;
};
)

I want to be able to change the arguments of .inclusivelyBetween
without it messing up the timing. I think the spooky action at a distance way of working is a nice way to make this work. The downside is the the sheer amount of ~variables to keep track of. Especially when doing this a bunch of times in the same environment. The upside though is that it gives great deal of flexibility and opportunities for live coding.

I’m just getting into writing classes so I’ll probably try to make this whole thing more neat. Like, this thing:

	shiftedArray=array.shift(-1, 0);
	array=shiftedArray;

Is ugly to me. Thinking of writing a new method for Array so that I don’t need this specialized shiftedArray variable.

Oh ok, but that’s easy:

	source_ { arg argSource;
		source = argSource;
		if(routine.isNil) {
			routine = Routine { |inval|
				loop {
					inval = source.embedInStream(inval)
				}
			};
		};
	}

So now it will create the routine only once. If the routine is in the middle of a pattern when source_ is called, it will finish that pattern before checking source again.

So the class design is independent of the behavior. You can get the desired behavior with a loosely encapsulated, hard to maintain, hard to debug mess, or you can get the desired behavior with clean encapsulation. That is: the desired behavior doesn’t magically make “spooky action” a good idea :laughing:

It is (in my opinion) still a bad idea, and not required to make it work.

Eventually you will have to change the design to deal with that problem. You could start with a better design and save trouble later, or start with a less capable design and have to retrofit later. (Or you might get away with it, but experience suggests that is unlikely.)

You can simply write array = array.shift(-1, 0); – you won’t need special methods for this.

In a loop, you wouldn’t hesitate to write i = i + 1 – array methods are no different.

hjh

Okay you’ve got me thinking. I’m gonna restructure things. Somehow . The type of composition I’m interested in is based around having “open” variables to some degree though. Using them within other functions to change that functions behavior and vice versa. I like the idea of having different functions interact with each other. Making them change the behavior of each other at a call per call basis.

I know It’s most likely “bad” programming practice to be sloppy with encapsulation but to my specific purposes It’s really useful.
It could be a bad idea for sure, SC and coding is fairly new to me in contrast to yourself, but at the moment though it works like I want it to.
Gonna need to find a way to combine the two though, flexibility and reliability. Looking into Environments to deal with the char-count.

Haha, yes. This is how I wanted to do it. When I was setting this mess up I had some trouble with the behavior of .shift and doing that convoluted variable conversion thing seemed to solve it. Can’t remember exactly what was going wrong though. Glad to find it was user error though.

Oh missed a bunch of good environment stuff in here… going to look into this!

Have you looked into ProxySpace, and Tdef?

A little suprised no one has mentioned these yet… though I believe you make get similar behavior with using Prout, and embedding enviornment variables that you call within the pattern routine, and while changing the values of outside of it.

This is a very common form of desired usage… to change variables on the fly… so common and desired, in fact, there’s a huge section of SC dedicated to it… take a look at Live Coding

Hmm, ProxySpace yes, but not Tdef. Looks interesting.
Could be a nice way of organizing things.

Another interesting (but well-hidden in the documentation) trick is one can call:

currentEnvironment.know_(true) // or .know = true

put the environment in an interpreter variable:

this.n_(currentEnvironment) // or simply n =

and now, you can:

n.embeds_(<pattern>) // returns just like ~embeds

It’s useful if you happen to prefer the .setter_ form of defining variables…

Really? This is from Tdef help source:

Pdefn provides an interface to its superclass TaskProxy.Tdef keeps a reference to a task (time pattern) that can be replaced while playing. It continues playing when the old stream ended and a new stream is set and schedules the changes to the beat. One Tdef may be used in many tasks in different places. A change in the task definition Tdef propagates through all tasks.

…could have sworn that was what you were looking for…

Either way, good hunting!

…and one more thing, I’ve recently fell in love with .addUniqueMethod

It’s by far way easier than writing classes… works like this:

object.addUniqueMethod(\method_name, { |object x, y, z| })

This works for all objects, so long as .respondsTo(\method_name) is false

They must be unique methods

… & in the arg list, |object| is always the first argument, and always refers to the original object … the rest of the args depend up to you… and if you didn’t know:

|obj …x| will collect all of the arguments (no matter how many) in an array, and you cant print them all using:

Post <<* x // x being a collection

Peace man later

Oh wow I think I get it now.
So this is the “correct” way of expressing it?

~embedder=Embedder.new;

~embedder.source_(Pseq([1, 0, 0, 0, 1, 0, 0, 0],1));
~embedder.source_(Pseq([1, 0, 0, 0, 0, 0, 0, 0],1));

(
var array=0!9;
~hhAmpArrayGen= {
	var pat1;
	array=array.shift(-1, 0);
	pat1=~embedder.next;
	array.put(array.size-1, pat1);
	array;
};
)

~hhAmpArrayGen.value

This was an eye opener. I’m gonna try to develop this further. Thank you.

:wink: A few advantages to this approach:

  • Multiple, separate namespaces, forwarding data to completely independent embedders.

  • Debugging: You can track down the embedders listening to a given environment by theEnvironment.dependants (where the function references to arbitrary variables are untraceable).

When things are simple, it won’t make much difference. When you’re trying to run 10 complex patterns at once, with multiple variables per pattern, organizing the objects will become more valuable.

Or the equivalent ~embedder.source = Pseq([1, 0, 0, 0, 1, 0, 0, 0],1); which is closer to the assignment syntax you had preferred.

hjh