"Variable '%s' not defined." or "Variable '%s' not declared."?

Hello!

I have a very basic question about an error message.
(I have no idea why this question is on my mind now rather than before…)

Evaluating the following code

(
aValue = 3; 
aValue +3 
)

will return the following error:

ERROR: Variable 'aValue' not defined.
  in interpreted text
  line 1 char 11:

  aValue = 3 
             
-----------------------------------
-> nil

I suddenly thought that the following message would be better:

ERROR: Variable 'aValue' not declared.

Then I read the help document again and I am sure.
Is there a reason why the term “defined” is used here? Or is my understanding of variable declaration and assignment wrong? They do not seem to be the same even in a dynamically typed language…

I think the relevant part is the following line:

There is another line in the same file with the same strings, but I am not sure if it is also related to this question:

This reminds me of the English expression “splitting hairs,” which refers to a micro-distinction that is not really meaningful, but to which a lot of attention is being paid.

Practically speaking in SC, there’s no meaningful distinction between declaring and defining a variable.

FWIW the help document on variable scope and closure uses both terms (even within the same sentence :laughing: ):

  • “A function can refer not only to its own arguments and variables, but also to those declared in any enclosing (defining) contexts.”
  • “Note that even though the function which defines curVal and stepVal has completed execution…”

I don’t think there’s a strong basis to change it tbh. It’s harmless to change it, harmless to leave it as it is.

hjh

1 Like

My first thought was:

*Declaring a Variable: it would be like telling the computer, “I’m going to use a variable with this name.” But you don’t give it a value yet. It’s just a heads-up.

Defining a Variable: This is when you actually create the variable and give it a value. It’s like saying, “Here’s the variable, and this is what it should store.”

So you can try to define a variable that has never been declared. Or declare a variable and never define it.

But you’re also right, I don’t know where this distinction could help someone. Maybe the interpreter or linter could warn about unused declared variables, that would be useful distinction.

In some contexts, even a function that one defines and is never used anywhere could trigger a useful (or rather annoying) warning.

1 Like

Just found: c++ - What is the difference between a definition and a declaration? - Stack Overflow

There is a meaningful distinction for languages like C(++) where entities can be declared in multiple source files but defined only once.

SC isn’t such a language, so I think what I said still holds true for SC. I think in SC it is not possible to declare a variable without defining it, according to the linked explanation.

hjh

1 Like

It can be an idea for a LSP (optional) plugin: warning about unused variables, as a tool to clean up the code.

The corresponding code block is as follows:

(
var makeCounter;
makeCounter = { arg curVal=0, stepVal=1;
     // return a function :
    {
        var temp;
        // temp is local to this function, curVal & stepVal in the
        // enclosing function are referred to here within.
        temp = curVal;
        curVal = curVal + stepVal;
        temp                       // return result
    }
};

I would also like to add my understanding.
In this example, the following extracts are the relevant parts:

  • arg curVal=0, stepVal=1;
    I think that there is a declaration and an assignment of value at the same time. So, to shorten the explanation, the term definition is appropriate and understandable.

  • var temp;
    This is a fair declaration without assigning a value. So the definition does not seem to fit perfectly.

  • temp = curVal;
    curVal = curVal + stepVal;
    

    These two lines are just assigning values. The term definition may be applicable, but not the best, I think.

Back to the error message and the corresponding code example with comments:

(
aValue = 3; 
aValue +3 
)

To avoid the error, it should be

(
var aValue; // declaration
aValue = 3; // assigning a value
aValue +3 
)

or

(
var aValue = 3; // definition: declaration and assigning a value simultaenously
aValue +3 
)

Even considering the latter, the declaration seems to be the core cause of the error…

I’ve never had a problem with this error message in the past; I’m honestly not sure if that’s because I haven’t paid attention to it, or because I’ve never had a problem with it, so we can leave it alone. However, wouldn’t it be easier, especially for a newbie, if the term was changed since the term ‘declaration’ should be used to describe the solution anyway?

You’re not wrong, but I think many will consider it a minor detail. It’s up to the dev group.

As James said, if you had to declare the type of the variable, this would require a more informative message.

1 Like

It introduces the name as referring to a variable, allocates a stack slot, and assigns the value nil implicitly.

Assignment statements are not variable definitions in SC. They are just assignments.

There are many ways you can phrase it, I think people can figure it out easily the way it is now. “Declaration” is more common in documentation but there’s really not a clear – in the sense of formally specified – terminology for SC so they can be (and are) used more or less interchangeably IMO.

This is not a meaningful distinction because of the above; there is nothing “deeper” to analyze.

2 Likes

I stand corrected, if it introduces and allocates memory for the variable, initializing it with a default value like nil, then it blurs the distinction, declaring and defining a variable occurs simultaneously.

1 Like

Thanks for kind explanation!
Now, I am convinced. Only one thing I would like to say is related what @smoge wrote:

I find it a bit strange to say

  • it … assigns the value nil implicitly.
  • initializing it with a default value like nil ,

Doesn’t the variable remain nil because it is not assigned a value, i.e. it is not initialised?
Look at the following example:

// step 1:
(
a = 1;
b = 2;
thisProcess.recompile;
)

// step 2:
a       // -> nil

b       // -> nil

a == b  // -> true

a === b //-> true

nil is the singleton instance of the class Nil which represents uninitialized data. In help document, it is stated as follows:

Nil has a single instance named nil and is used to represent uninitialized data, bad values, or terminal values such as end-of-stream.

From this point of view, the distinction between definition and declaration does not seem entirely useless, but they are really too subtle and I am not even sure that I am following my own thoughts logically…

Anyway, I think the distinction between definition and declaration is not that crucial, and the error message does not seem to need to be affected.

1 Like

Oh, that’s why one can’t have a dictionary with value nil

“Uninitialized” means that the memory allocated for the variable is not changed – not set to anything specific – i.e. garbage data, which, if operated upon, can cause bad results or even crashes.

SC sets the memory to a specific value nil. This is different from “uninitialized.”

hjh

1 Like

Ah, Thanks! Now, the whole thing is cleared!

Exactly, there is a difference between “representing uninitialized data” and actually being uninitialized. nil is a value, and the variable is initialized with that value.

@smoge - nil in events and dictionaries did confuse me for a long time. I was doing nil checks on events and dictionaries, like:

a = (somekey: 3);
a.somekey = nil; 
a.somekey == nil; // first thinking the keys was still there
a.keys; // ...but realizing the key is gone now

The handling of nil still is a little hazy to me. So I guess from a sclang user point of view, there is no way of ‘knowing’ if a value returns nil because it has never been initialized or because it has been declared and either not been assigned a value or explicitly assigned the value nil.

a = [1, 2, nil];
a[2]; // nil
a = [1, 2];
a[2]; // nil

I don’t really have any extensive experience using other programming languages than SC. Is the handling of nil in SC the same as in similar languages?

Smalltalk and Lua both do this as well. In Lua you can assign nil to a variable to delete it, since the global environment is just a table and assigning nil as the value of a table key deletes the association.

Variables hold values, not return them; and all variables are initialized in SC. The only distinction to be made is a syntactical one, whether you write “var x = nil” or just “var x”. It is purely a difference of syntax, and the semantics and bytecodes in each case are the same.

2 Likes

What I meant was that if you just open SC and type ‘~something’ or ‘x’ and hit return, nil is returned. Is that internally a different situation than first doing ~something = nil or x = nil?

Perhaps an opinion, but I think this is one of those cases where, if you want to go looking for differences, you can find them, but it might not be a meaningful exercise.

There is one difference here that is meaningful: ~something vs x.

x is an interpreter variable. This is declared in Interpreter (and, as noted above, initialized to nil):

Interpreter {
	// The interpreter defines a context in which interactive commands
	// are compiled.

	var <>cmdLine; // place holder for text executed from a worksheet
	var context; // faked interpreter context frame. Don't mess with it.

	// a-z are predefined variables for use by the interactive context.
	// They are read+write so that programmatic methods can
	// get and alter the values that the interpreter has access to.
	var <>a, <>b, <>c, <>d, <>e, <>f, <>g, <>h, <>i, <>j;
	var <>k, <>l, <>m, <>n, <>o, <>p, <>q, <>r, <>s, <>t;
	var <>u, <>v, <>w, <>x, <>y, <>z;

	...
}

So if you just open SC and run x, it will look up the variable declared here and return the auto-initialized value nil.

~something (i.e. any environment variable) is a member of a collection, the currentEnvironment. When you first start SC, the current environment is topEnvironment, and this environment is empty.

In SC, if you look up a collection entry by name or index and that name/index doesn’t exist, the result is nil. (For arrays, some other programming languages would throw an “index out of bounds” error in that case, but SC doesn’t.)

So in your specified case (first thing to run in SC is ~something), the topEnvironment collection is empty, and remains empty, and you try to look up \something in it, and it’s not found, and the result is nil.

If you do ~something = nil, then it will try to .put nil in under \something. This will fall through to the line value ?? { this.removeAt(key); ^this }; – if the value is nil, remove the key. But the key doesn’t exist, so removeAt simply returns without altering the collection. That is, there is no “something” that gets created and then deleted. It didn’t exist initially; it doesn’t exist at the end; and it doesn’t exist anywhere in the middle either.

This might be confusing WRT declaration or definition – but, remember that environment variables are members of a collection. They are not and were never variables, so there is no “declaring” them. ~something is only a shortcut syntax for \something.envirGet. Using environment variables is only manipulating the collection – putting things into it, getting things out of it – no declaration needed.

hjh

1 Like

@jamshark70 - thank you, that was very informative and helpful.