Short dumb question re: syntax

What would one say about this “technique” or “method” of naming a variable, passing it an argument, then defining the variable’s function later?

Like “make” here being given the |list| from Dialog:

(
var make;
var dialog = Dialog.openPanel({ |list| make.(list) }, nil, true);
var win = Window("Sample Player 3000").front;
var samples = ();
var buttons = ();
make = { |paths|
	{
		paths.do{ |path|
			[\loaded, path.basename].postln; ...
.............................cut the rest .....................................

Maybe it doesn’t have a name shrug

I tend to find that setting a bunch of vars where I define them (e.g. var x = ....) gets very hard to read for complex functions - usually when things get at complex as what you have in your example, I’ll break things out into JUST the definitions, and then set them after everything is defined.
There’s no functional difference between the two, so it’s mainly a matter of taste and readability.

Oh, I misread! Yes, what you’re doing also works. I tend to think this can get a little hard to read since it’s set AFTER it’s first referenced. It works because SC functions capture the live scope they are a part of, meaning they’ll see it’s current state if you change it later - but e.g. in Javascript this would break. If you’ve made this mistake in Javascript once or twice (it results is very weird things…) you tend to train your brain to avoid the concept entirely.

1 Like

Yea… don’t do that, its rather confusing to read and in this case, unnecessary… its much more common to define all the functions at the top, then piece them together at the bottom of the scope.

It might be necessary with some complex recursion though…

~f = { |n|  if (n<=0, n + 1, {n + ~g.(n - 1)}) };
~g = { |n|  if (n<=0, n - 1, {n + ~f.(n - 2)}) }; 
~f.(10)
1 Like

Maybe it doesn’t have a name

Perhaps “un-typed mutable reference”? It’s ubiquitous!

var f = nil, g = { f };
f = 1;
g.value // 1

Mutate f in g!

var f = nil, g = { f.postln; f = 2 };
f = 1;
g.value; // -> print 1
f // -> 2

Ps. Functions are just objects:

var f = nil, g = { |x| f.value(x).postln };
f = { |x| x * x };
g.value(4); // 16
f = { |x| x * x * x };
g.value(4) // 64

Also in Js:

(function() {
  var f = null, g = function(n) { return f(n); };
  f = function(n) { return n * n; };
  console.log(g(4)); // 16
  f = function(n) { return n * n * n; };
  console.log(g(4)); // 64
})()

& in Scheme:

((lambda ()
   (let* ((f '()) (g (lambda (n) (f n))))
     (set! f (lambda (n) (* n n)))
     (display (g 4)) ; 16
     (set! f (lambda (n) (* n n n)))
     (display (g 4))))) ; 64

& in Python:

f = -1
g = lambda x: f(x)
f = lambda x: x * x
print(g(4)) # 16
f = lambda x: x * x * x
print(g(4)) # 64

&etc.

1 Like

Am I confusing terms, or doesn’t that make SC like semi-lazy? Since it’s not evaluating g yet, … or is it evaluating with f as nil, and that’s why you have to define it? yeah nvm I see

  • That Scheme code looks funny TBH. just an opinion

To me, thsi si why Haskell is so dang beautiful and impressive, but, I can also see with my very limited knowledge, how a l;ot of stuff can be done way easier in imperative languages with side effects (although all these people talking about how monads are so mystical and Haskell has no side effects, blah…couldn’t one just say Monads are functions that make side effects, without getting into the weeds? lol. I probably dont know what I’m talking about, but from what I can tell after looking briefly into category theory, monads are just like an interface between immutable functions and the outside world using categories as a mode of reference…something like that)

Thank you thank you! That’s pretty hard to Google when you dont have the vocabulary to do so!

When f is created (e.g. when the line of code where its defined is executed), because it refers to g, it receives a reference to the outer scope as a data structure. This is a refernce, not a copy, so if things in that scope changed that change will be visible when f is executed. You might imagine this reading something like frameWhereIWasCreated.g rather than g.

At risk of muddying the waters – there is a highly amusing loophole in SC’s parsing of variable declarations.

(
f = {
	var b = a + 1;
	var a = 1;
	[a, b]
};
)
-> a Function  // it didn't choke :-O

f.value;
-> [ 1, 2 ]

It must be that the parser first figures out all the variable IDs that are being declared within this scope, and then attaches the initializers.

(However, this will execute only if the forward-referenced variable is initialized to a literal. If it’s, say, var a = 10.rand; instead, the function still compiles, but a + 1 fails because a is nil at that point.)

hjh

@rdd

var f = nil, g = { |x| f.value(x).postln };
f = { |x| x * x };
g.value(4); // 16
f = { |x| x * x * x };
g.value(4) // 64

This just returns nil each time in SC ?

Because g was declared as a var, then the example must be run as a block. You can’t split this one up into single lines.

hjh

1 Like

… doesn’t that make SC like semi-lazy? Since it’s not evaluating g yet …

Yes, that’s exactly the connection!

In SuperCollider conditional evaluation is written something like:

+ True { ifThenElse { arg q, r; q.value } }
+ False { ifThenElse { arg q, r; r.value } }

and then p.ifThenElse({ q }, { r }).

The receiver (p) and the message arguments ({ q } and { r }) are evaluated before performing the message send (c.f. applicative-order evaluation).

Hence the lovely and concise notation for writing procedures of no arguments!

In Haskell conditional evaluation is written something like:

ifThenElse True q r = q
ifThenElse False q r = r

and then ifThenElse p q r.

Haskell won’t evaluate any part of any expression unless and until it’s required (c.f. normal-order evaluation).

Haskell does something quite like rewriting each expression e as { e } and implicitly sending value messages.

In addition it replaces { e } with it’s answer when it’s first evaluated, so the work only happens once, something like:

var memoize = { arg f; var r; { r.isNil.if({ r = f.value }); r } };
var f = { arg c; "f!".postln; 42 };
var g = memoize.value({ "g!".postln; 84 });
[f.value, f.value, g.value, g.value] // prints f! twice and g! once

There are very nice aspects to both normal-order and applicative-order evaluators!

Ps. About:

…in imperative languages with side effects…

Haskell has a very simple mechanism for attaching an effect to an expression (c.f. unsafePerformIO) but in general it’s very hard to know if, or when, or how many times such effects will be evaluated, hence all the libraries (Control.Applicative, Control.Monad &etc.).

Applicative-order evaluation is a very nice sequencing construct!