Syntax of using key-value pairs as input to function

I am trying to store the args to a function in an event and calling the function with the key-value pairs but I can’t figure out the syntax:

~func = {|val1 = 4, val2 = 3|
	val1 * val2
};

~func.(val1: 2, val2: 5); // works, yields 10

~args = (val1: 2, val2: 5); // i want to store the args and value in an event (or array)

~func.(~args.asKeyValuePairs) // not working. What method can place the key-value pairs in the call to the function?
~func.valueWithEnvir(~args)

If this was an array with unnamed args you could also do this…

~argsArray = [2, 5]; 
~func.(*~argsArray);
1 Like

@jordan - great, just what I was looking for:) Follow up question, how can I construct a case from events?:

(
~cond = (one: false, two: true, three: false);
~action = (one: \one.postln, two: \two.postln, three: \three.postln);


case
{ ~cond[\one] } { ~action[\one] }
{ ~cond[\two] } { ~action[\two] }
{ ~cond[\three] } { ~action[\three] }

// How do I construct the above case from the events without writing it out one by one?
)

Ah, this might not be the right approach.

Is it required that only one of the states (one, two or three) be set to true at once? If not, the case clause will not work.

If you actually want to associate a message with an action I’d just pass the keys directly to the function, since you will need them to set the ~cond state anyway.

You code also won’t let the caller change the order, but this will. This might be good or bad depending on what you are trying to do!

~actions = (
	one: {\one.postln}, 
	two: {\two.postln}, 
	three: {\three.postln}
);

~dispatch = {|...keys|
	keys.do{|k| 
		~actions[k] !? _.() ?? {
			postf("could not find key: %\n", k)
		} 
	};
}

~dispatch.(\one);
~dispatch.(\blah);
~dispatch.(\one, \two, \three);

Maybe I oversimplified the example at bit: The conditions will be functions and there is no way of knowing how maybe will return true at any given time. What I am looking for is a way to construct a case with an arbitrary number of conditions and actions, ideally in a way where I can rearrange the order of the condition - actions to decide the order of testing inside the case.

Maybe I am better of handling it with a function. This is the behavior I am looking for:

(
~cond = (one: false, two: true, three: false);
~action = (one: { \one.postln }, two: { \two.postln }, three: { \three.postln });

~case = { |cond|
	var flag = true;
	cond.do{|n|
		if (flag)
		{ if (~cond[n] ) { ~action[n].(); flag = false } }
	}
}
~case.([\one, \two, \three])	
)

Here’s one way:

(
~cond = (
    one: true,
    two: false,
    three: true,
);
~funcs = (
    one: { "one".postln },
    two: { "two".postln },
    three: { "three".postln },   
);
~results = merge(~cond, ~funcs, {
    |cond, func|
    if(cond, func)
});
)

@scztt - thanks. You example does not behave like case, as it both outputs \one and \three…it should not get to \three since \one is true.

Ah sorry, I misread - I thought you wanted all of them to execute. Be careful here: Dictionaries are not ordered, so in your usage it is undetermined which of \one or \three is executed. Maybe try something like this? Sorting the keys means you’ll get the same result every time.

(
~cond = (
    one: true,
    two: false,
    three: true,
);
~funcs = (
    one: { "one".postln },
    two: { "two".postln },
    three: { "three".postln },   
);

~keys = ~cond.keys.asArray.sort;
~funcs[~keys.detect({|v| ~cond[v] })].value();
)

If you look at my ~case function above you will see that it is ordered.

Yes, I was mostly thinking of your comment // How do I construct the above case from the events without writing it out one by one?. Somewhere - whether in a case, or in an array of manually or automatically sorted keys - you’ll need to define an order to visit the keys. Since you don’t need random access to the conditions, you could also use an array for this data structure, e.g. [\one -> true, \two -> false, \three -> true] - then at least you don’t need to maintain two lists of identical keys, you can just detect directly on this one.

1 Like

Actually this function is pretty good. scztt referred to detect, which will simplify the function and remove the need for the flag variable.

  • do means “always touch every element in the collection.”
  • detect means “start looking through the collection, and when you find what you’re looking for (condition == true), stop and don’t touch any more elements.”

So in your case, \two is true, but the do loop will go ahead and check \three as well. detect would stop after two.

~case = { |cond|
	var whichOne = cond.detect { |n|
		~cond[n]
	};
	if(whichOne.notNil) { ~action[whichOne] }
};

A question: Will the ~cond items change often, or is there a relatively stable association between a condition and an action? Currently ~cond is storing Booleans directly, so, presumably you would be rewriting this collection often. But if ~cond consists of functions (like case) where the Boolean results are variable, then it might be better to collapse ~cond and ~action into a single collection of associations between conditions and actions. (Common data-structure-design problem: Should it be multiple, parallel collections, or one collection of more complex objects?)

A bit of history: SC originally did not have switch and case – the only way to do these was with data structures:

// case
[
	{ condition } -> { ... },
	{ condition } -> { ... },
	...
	true -> { ... default ... }
].detect { |assn|
	if(assn.key.value) {
		assn.value.value
	}
};

// switch
(IdentityDictionary[
	value -> { ... },
	value -> { ... },
	value -> { ... },
	value -> { ... },
].at(thing) ?? { { ... default action ... } }).value;

The problem with this is that it’s slow to build a data structure. In many cases, it would be possible to reuse a data structure, but if the action functions depend on local or instance variables, then it may not be possible to build the collection once and share it across all instances. So JMc added special byte codes: case compiles as a series of “jump if false” instructions, and switch encodes the hash table of values into the function and accesses it with a “ControlOpcode” – but both of these are just performance optimizations for the data structures.

I think this is the genius of Smalltalk, and probably what attracted JMc to it in the first place: that everything we think of as a control structure can be implemented using polymorphic method dispatch and data structures – meaning that control structures are completely optional. This idea didn’t catch on because of the speed problem, but there’s something appealing about the purity of it (and once you grasp that, then problems such as the one in this thread become easier to approach).

hjh

2 Likes

Nice to know about detect, which also mean you do:

[ false, true, true].detect{|n, i|i.postln; n.().not }.isNil // = false && true && true
[ true, true, true].detect{|n, i|i.postln; n.().not }.isNil // = true && true && true

I still don’t understand why the above is not the standard way to calculate a boolean expression, why does SC need to calculate both expressions in

( { false.debug(\1) }.() && { true.debug(\2) }.() )

Wouldn’t only calculating the first expression be in line with the way case and switch work?

I am considering a data structure like this:

e = (
	elements: [\elem1], //...more elements
	elem1: (
		seq: [ \c1, [\c2, \c3] ], // [\c2, \c3] = case { c2 } {..} { c3 } {..}
		cond: (
			c1: [ { 2.rand.asBoolean }, { 2.rand.asBoolean } ],
			c2: [ { 2.rand.asBoolean }, { 2.rand.asBoolean } ],
			c3: [ { 2.rand.asBoolean }, { 2.rand.asBoolean } ]
		),
		argList: (
			c1: { (val: 1) },
			c2: { (val: 4) },
		),
		action: (
			c1: { |val = 100| [val, \func1].postln },
			c2: { |val = 0| [val, \func2].postln },
			c3: { |val = 9| [val, \func3].postln }
		)
	)
	//..more elements
);
)

(
e.elements.collect{|elem|
	e[elem][\seq].collect{|key|
		case
		{ key.isSymbol } 
		{ 
			if ( e[elem][\cond][key].detect{|n, i|n.().not }.isNil ) // bool && bool
			{ e[elem][\action][key].valueWithEnvir(e[\elem1][\argList][key].()) }
		} 
		
		{ key.isArray } // case
		{
			var k = key.detect{ |key2|
				e[elem].cond[key2].detect{|n, i|n.().not }.isNil // bool && bool
			};
			if (k.notNil) { e[elem][\action][k].valueWithEnvir(e[elem][\argList][k].()) }
		}
	}
};
""
)

This would be a pretty efficient data structure as expression are checked on a need to know basis. A new preset could overwrite or add to the base element and the base element would always be called in between new presets to reset.

Could I get in trouble with the selector table if I had many elements in e, with many different functions? Any other thoughts on the structure?

Computers evaluate expressions reverse-Polish style. Yours translates to:

  1. Push function { false.debug(\1) }.
  2. Value it – because of .().
  3. Push function { true.debug(\2) }.
  4. Value it – because of .().
  5. Do && on the two resulting values.

So, in your specific example, the reason why the second function evaluates it is because you explicitly requested it.

TL;DR the best ways to write AND and OR in SC are:

false.debug(\1) and: { true.debug(\2) }

false.debug(\1) && { true.debug(\2) }

Both do short-circuit the second function.

C-style short-circuiting of && and || is compiler magic. && translates to: If the first operand is 0, skip over the second operand (and || is, skip it if the first is nonzero). This means && and || are a hybrid of a math operator and a control structure.

But, as noted in my last post, in SC’s Smalltalk roots, conceptually there aren’t any control structures – only method dispatch and first-class functions. In practice, there are a few control structures because method dispatch is slower than “jump” byte codes.

“There aren’t any control structures” is a radical, mindbending idea for anyone raised on Pascal / C like I was… but:

// in the True class
	and { arg that; ^that.value }

// in the False class: here's the runtime short-circuit!
	and { arg that; ^this }

There’s no compiler magic here. Short-circuiting happens as a result of dispatching based on the first Boolean value (and depends on the second operand being a function – which is consistent with the general rule in SC that conditional or looped evaluation always requires passing a function somewhere, unlike C where some things that are not written as blocks behave as blocks, e.g. && second operand).

BTW, of “no control structures,” you might think while is impossible without a jump-back instruction, but:

+ Function {
    recursiveWhile { |body|
        if(this.value.not) { ^nil };
        body.value;  // edit here: first version was wrong
        ^recursiveWhile(this, body)  // missed the recursive call the first time
    }
}

With early method exit and tail-call optimization (which SC has), while can be done recursively. This is slooooowwwww but drives home that conceptually, compiler control structures are not strictly required at all. Compiler control structures in SC are purely performance optimizations. There’s no other reason for them.

SC tries to look a bit like C, but it isn’t C.

I would just add elements incrementally in that case.

hjh

2 Likes

!!!
My jaw is on the floor a bit - after all these years, I didn’t know that && also did short circuiting when used with a function, though now that I think about it, it would be very silly for it not to. Time to remove all of the and:'s from my code…

I thought same as you, but tried it today, and… whoa.

I will still use and: because it’s one less method dispatch.

(
bench {
	100000.do {
		0.5.coin and: { true }
	}
}
)

time to run: 0.0094671350000013 seconds.
time to run: 0.0084242550000013 seconds.

(
bench {
	100000.do {
		0.5.coin && { true }
	}
}
)

time to run: 0.018513173999999 seconds.
time to run: 0.019635461999997 seconds.

hjh

1 Like

Ah, the extra method dispatch is a difference in the way it’s compiled?

True {
	&& { arg that; ^that.value }
	and { arg that; ^that.value }
}

Weirdly, this rather esoteric way of expressing it has about the same performance profile as and:

(&&) (0.5.coin, true)
1 Like

Yes, but in a different way from what I was thinking. The compiler treats a and: { b } – a binary-operator node where the second operand is an inline-able function – as a hybrid math-op-plus-control-structure:

{ true and: { false } }.def.dumpByteCodes;

BYTECODES: (6)
  0   6C       PushSpecialValue true
  1   FA 00 01 JumpIfFalsePushFalse 1  (5)
  4   6D       PushSpecialValue false
  5   F2       BlockReturn

But && compiles as a standard method dispatch:

{ true && { false } }.def.dumpByteCodes;

BYTECODES: (7)
  0   6C       PushSpecialValue true
  1   04 01    PushLiteralX instance of FunctionDef - closed
  3   B0       TailCallReturnFromFunction
  4   A2 00    SendMsg '&&'
  6   F2       BlockReturn

I guess I was thinking of the way that % is defined in Object, and dispatches through mod, so mod(a, b) is one dispatch but a % b is two.

In fact, though, here we have 0 dispatches, vs 1. :laughing:

This example, however, removes the short-circuiting. In this case, the second operand is so simple that the function dispatch to evaluate the short-circuiting version is more expensive than just pushing true directly onto the stack. If I put that back in, then I get comparable results to my original test.

hjh

Thank you so much, a ton of useful information here, still trying to absorb it all.