Postln appears to affect results

Hello all,

I have encountered some strange behaviour with this code.

When I run the code with the “result.postln;” the “branches[7].postln;” shows what I would expect to see.

[ 0, 0, 4, 6, 7 ]

However, if I remove the result.postln then I get this…

[ nil, nil, nil, nil, nil ]

This makes very little sense to me, as I thought the postln has no effect on the code other than to display certain results.

The code is actually working, but this bothers me as I should be able to remove that line.

Any guidance very much appreciated!

(
var highestPitch = 10000; var lowestPitch = 0;
var range = highestPitch - lowestPitch;
var numNodes = 5; /// Number of sections
var sections = Array.fill(numNodes,{arg i; (2 ** i).asInteger}); /// Number of harmonics
var freqGap = range / sections[numNodes-1];
var completeHarmonics = Array.fill(sections[numNodes-1], {arg i; freqGap * i + lowestPitch}).asInteger;
var branches = Array.fill(completeHarmonics.size,
	{ |harmonic|
		Array.fill(sections.size,
			{|node|
				var result = harmonic.trunc(sections.reverse[node]);
				result.postln;
	})
});
branches[7].postln;
)
1 Like

Quite the contrary. result works just as before. .postln does not affect anything other than posting. When you delete result.postln your function returns nil, since there is nothing there anymore.

1 Like

Thanks for your reply Smoge. My issue is that when I delete .postln the “branches[7].postln;” does not return the same result.

That I cannot understand, as I do not see the connection.

1 Like

In your code, the result of Array.fill (which runs n times) is stored in the branches variable.

branches[7] shows the item at index 7 of the array.

If you delete the last line, only the postln inside Array.fill will show an output.

Note: post and postln just tell you what’s happening in your code; they don’t change anything. They are not stored anywhere, it’s just a tool for debugging.

var branches = Array.fill(completeHarmonics.size,
	{ |harmonic|
		Array.fill(sections.size,
			{|node|
				var result = harmonic.trunc(sections.reverse[node]);
				result.postln;
1 Like

A function returns the result of its last expression.

var ... anything looks like it might be an expression, and if there’s an expression after the =, then it will execute byte codes like it’s an expression… but formally, it isn’t an expression.

You have to have at least one thing after the list of vars.

If you have branches[7].postln at the end of the function and you delete the whole line, then there is nothing after the vars and you get nil.

If you have branches[7].postln at the end of the function and you delete only the .postln, leaving branches[7] in place, then the function will have a result.

hjh

1 Like

Yes, but it’s not even a function, although it “functions” like a function ))

Let us know if that’s clear, @domaversano. No worries if it’s still confusing for you.

Actually it is a function – the result of thisProcess.interpreter.compile("... your block of code here ...") is a real Function object. Every interactive block of code is compiled to a Function, then evaluated by .value just like any other function call.

hjh

In a way, data and function are not that different… Every function is also data. Forget lambda calculus, just figure a simple example we use in audio: it’s common practice to convert a function int table lookup for low level optimization. If a data is passed as a function by the language, it’s not that different. There is nothing special about it being a function fundamentally.

Thanks again for the replies. They are greatly appreciated. I’ve not yet had the moment of enlightenment.

If I follow this correctly the nested Array without the “result.postln;” is not returning anything because it is not an expression. Yet, it does return something when the “result.postln;” is there… Which I still don’t get how that chimes with what Smoge has written which is my understanding of things…

Note: post and postln just tell you what’s happening in your code; they don’t change anything. They are not stored anywhere, it’s just a tool for debugging.

If I understand correctly - and I am not confident that I do - I should have an expression, rather than var result = … in the final line of that function.

Perhaps to make things clearer what would that expression look like? I´m not used to thinking about functions in this way. I am sure it will make total sense soon, but on the face of it it still feels very irrational.

Thank you both for your good will and patience, I really appreciate it.

In this context, that it’s a function could be thought of as an implementation detail: rather than have one code path for function evaluation and a separate code path for interactive code blocks, SC uses the first as the mechanism to handle the second (so there’s just one code path). It also means that we don’t have to think of code blocks as a different type of entity.

hjh

1 Like

Try to forget everything about “postln”, it’s not relevant.

In a way, it’s understandable why you’re confused. I give you some examples:

In Python, for example, a real function, with inputs and outputs, use the keyworkd return. That’s more clear.

def my_function(x):
  return x + 1

Or Haskell

add :: Int -> Int -> Int
add x y =  x + y

But not in SuperCollider, a proper function has clear input after the keyword arg. But not the output, the output is just the last line.

1 Like

The issue is that you’re deleting the entire line. You don’t want to delete the whole line. You want to delete only the postln… only the dot and the postln keyword. Do not delete the postln receiver!

Why are you deleting the result?

hjh

2 Likes

Ahhhh, I get it. I am glad I ran into this problem, because I have always been somewhat mystified about how a function returns its output. Thank you so much for your patience, the moment of enlightenment arrived. I appreciate you writing out the examples as well, that helped clarify things.

1 Like

Your response combined with Smoge’s makes it make perfect sense. The postln was distracting me from the need to keep the rest of the line there. Thanks JamShark!

1 Like

It’s kind of confusing to start working with SC, there are very specific concepts to grasp, different ways to do same things, and it’s also a “niche” language with less info around.

No worries, just keep going and it will worth it! It’s special and powerful if you give it some time.

Just to clarify because the definition of where the ‘last’ line is, is actually quite confusing if your new.
It would be more precise (and confusing) to say the result of the last expression is returned.

Consider this…

~f = { |input|
	var a = input + 1;

	if( a > 10 ) {
		if(a.mod(2) == 0) {
			\here
		} {
			\there
		}
	} {
		\everywhere	
	}
}

The outputs are either \here, \there or \everwhere, none are on the last line, but they are all potentially the result of the final expression in ~f.

The if expression returns the final expression of the branch (function) it executed.

To make this clearer, sometimes you will see if expressions written like this…

if(a.mod(2) == 0, {\here}, {\there})

… which more clearly communicates it is a single expression.

In some other languages you might hear the if expression called a ‘statement’, var is one of the few statements we have in supercollider and as a result, doesn’t return. Also, in some other languages var does return the value (what else could it return?). It is very confusing that it doesn’t return and you are right to be mythed! Unfortunately fixing it isn’t really possible.

2 Likes

It is a legit point of confusion: var with assignment returns nothing, but assignment to a previously declared var does return! One of the few things in SC where I can’t think of any justification at all… usually I can come up with at least a strained rationale, but this one… nope. It’s a banana peel with no reason to be on the floor.

f = {
    var a = 10.rand;
};

g = {
    var b;
    b = 10.rand;
};

[f.value, g.value]
-> [ nil, 3 ]

hjh

1 Like

You’re right, sorry, maybe not the very “last line,” but definitely the last piece that tags along after all its companions have had their say. ))))

Curiously, it brings up something quite interesting. It resembles the introductory examples that Peter Landin brings up in his paper “The Next 700 Programming Languages.”

In contrast to the norm in “formal” and logical languages, it’s not something idiomatic anywhere else! Our verbal communications or mathematical demonstrations often take a different organization. Nothing more common than a “where-clause”. That “where…” explanation sounds just right on the right side of something it is just supporting.

Those abstract conventions can lead to different outcomes. It’s a fascinating paper.

If I’m not mistaken, in JMC’s brand new language, EVERYTHING sort of rolls in right-to-left eventually resulting in a single array!

I’m not even sure how that would inspire the composer/programmer!

I suppose it would kinda be useless in supercollider as its doesn’t really make sense to assign and return in one go, and the other places you might use it are dealt with other patterns.

e.g.
In other languages it is nice to do this…

...some code...
let a = if(let result = tryGetThing()){
   result + 1
} else {
   0
}

… but in supercollider, to literally do it, that would look like…

...some code...
var a = {
   var result = tryGetThing.();
   if(result, {result + 1}, 0)
}.()

… but we already have this…

...some code...
var a = tryGetThing.() !? (_ + 1) ?? 0;

… a little confusing at first and requires nil to be returned for false, but covers the same usecases.

But this is syntactically legal (although in SC, the if requires the result to be Boolean, which will not be ok with +… so I’ll modify that and assume the result is numeric):

var result;
var a = if((result = tryGetThing.()) > 0, { result + 1 }, 0);

I used to write like this all the time, but eventually came to the conclusion that variable assignment in the middle of other constructions is poor for readability, so I stopped.

hjh

1 Like