The data that computer programs deal with comes in a variety of shapes and sizes, and will be of different types. The very common examples are strings, which represent sequences of characters, like the text you are reading, and numbers, which represent quantities. But there are more, and some computer languages allow programmers to design their own types1.
Computer languages deal with this by classifying the data they deal with according to a number of different type systems. Some languages have no type system at all, and just treat all data as sequences of bits. Other (higher level) languages have more or less strict type systems that are enforced at different times2.
The strictness of the type system will then be a result of what those rules are, when they are enforced, and whether there are any loopholes (= ways for programmers to ignore them and get away with it).
In this sense, Praat is a dynamically typed language, because there is a type system (which makes it typed), but these type rules are dynamically enforced, which means that data is type checked only when a particular statement is evaluated by the interpreter. So, while Praat makes a difference between string and numeric variables3, something like this is completely valid (but don’t do it!):
1 2 3 4 5 if 0 string$ = 1 ; Not a string! else appendInfoLine: "Safe!" endif
There is one more type in Praat’s current type system: although it is undocumented, and apparently not entirely implemented, Praat also supports (or “will support”) arrays.
Arrays represent an ordered sequence of values, each of which is normally subject to the same typing rules applicable in the rest of the language. It is difficult right now to say much about Praat arrays, because since they are largely experimental (and have been stuck in development for some time), it is not entirely clear what they will do, or what features will remain when they are finished.
The keen reader will have noticed that Praat already has something that fits the description above: a series of ordered values, each of which with a type. This is the case with Praat’s indexed variables. Aren’t these a different type? They certainly give rise to different error messages:
1 2 3 4 5 6 a = 0 asserterror Undefined indexed variable appendInfoLine: a asserterror Unknown variable appendInfoLine: a_14
But while this is true it seems to me to be more an attempt at writing useful error messages, rather than evidence of them being different types. And indeed there is no evidence of an “indexed variable” type in the code4.
Compare this with the errors raised by arrays:
1 2 3 4 5 6 7 8 9 10 11 a# = zero#(5) for i to 5 appendInfoLine: a#[i] ; Prints zeros endfor asserterror Row index out of bounds. appendInfoLine: a#[i+1] asserterror Found a numeric array expression ... instead of a numeric expression. a = zero#(5)
The truth is that in practice, indexed variables are little more than sintactic sugar, functionally equivalent to making a number of independent but similarly named variables. Which is not to say, of course, that this isn’t welcome5.
What about booleans? Even people with a passing understanding of computer programming will likely be familiar with the concept of a boolean variable: one that can only be true or false. Praat certainly looks like it has boolean variables:
1 2 3 4 5 6 true = 1 if true # Totally true! else # Not so true. More like false. endif
But once again, the value that stands in for the condition in that
is not a new type, it’s just a numeric variable. And any numeric variable
(including those returned from functions) can take its place. Once again, this
is confirmed by looking at the implementation.
But how do numbers map to boolean values? What numbers are “true”?
In Praat, any number which is not zero will be interpreted as a true value,
and consecuently, only
0 will be considered false. This makes it possible to
conveniently write things like this:
1 2 3 4 5 if numberOfSelected("Sound") appendInfoLine: "We have some sounds selected" else appendInfoLine: "No sounds selected!" endif
Some numeric functions reflect this by returning
0 when their results
should be interpreted as false (I’m looking at you,
index()). But this is
not entirely consistent, in that some functions (like
make an assertion on the selection (so they will crash if there is no selection,
or if the selection does not match the specified class).
In computer languages, this internal conversion between non-boolean types that are interpreted as boolean gives rise to what are called “truthy” and “falsy” values: they are not strictly speaking true or false, but they seamlessly become true or false when it matters.
What about strings? In many languages that use truthy values there are also rules to evaluate strings as boolean. Normally the empty string will be interpreted as false, and other strings will be true. But this is not the case in Praat: strings do not have a truthy value, so they cannot be used as conditions.
1 2 3 4 5 6 7 8 9 10 11 string$ = "something" asserterror Found a string expression ... instead of a numeric expression. if string$ # Bad endif if length(string$) # Good endif
Alternatively, the equality operator (which in Praat is confusingly contextual,
== will always mean equality, but
= will only sometimes mean
assignment) can be used to make a string comparison, and that will evaluate to
a truthy (= numeric) value.
1 2 3 4 5 6 appendInfoLine: 1 + ("a" == "a") ; Prints 2 string$ = "something" if string$ != "" # Good endif
What about the
undefined value? This is a numeric value (otherwise, we
wouldn’t be able to store it in a numeric variable), and it is arguably not
0, so you could imagine that it should be true. However, in Praat this
value cannot be used in conditions, and execution of a script will stop if the
undefined value is compared to some other defined value.
Note, however, that there is a difference between undeclared variables (which do not exist, and are unknown to the interpreter) and undefined variables (which exist, but don’t have a value):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 asserterror Unknown variable if mystery # How did we get here? endif mystery = undefined asserterror The value of the 'if' condition is undefined. if mystery # The mystery remains! endif if mystery == undefined appendInfoLine: "Finally!" endif
Actually… there is one context in which an undefined value can be directly
used in a condition: inline
1 2 3 4 5 6 mystery = if undefined then 1 else 0 fi if mystery appendInfoLine: "Undefined is true!" else appendInfoLine: "Undefined is false!" endif
So, based on what you know so far, what do you think the last snippet will print? Can you guess before running it?
Like Praat’s objects, at least as far as C++ is concerned. ↩
Those of you so inclined can see, for example, the implementation (at
the time of writing) of assignments to indexed string variables and those
of indexed numeric variables, and compare them to the assignment to
numeric arrays. You’ll notice that the first two are instances of
while the last one is an instance of
I die a little every time I see something like