Praat's type system
Typing
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
Arrays
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[15] = 0
asserterror Undefined indexed variable
appendInfoLine: a[14]
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.
Booleans
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
if
statment
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
selected()
)
internally 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,
in that ==
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 if
s.
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. ↩
-
Norman Ramsey regrets having to regularly write about this in StackOverflow, but what he writes is quite interesting. ↩
-
This difference was not actually there from the beginning. It was introduced sometime near mid 2000, around the release of version 3.8, which has been lost to time. ↩
-
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
Interpreter_stringExpression
andInterpreter_numericExpression
respectively, while the last one is an instance ofInterpreter_numericArrayExpression
. ↩ -
I die a little every time I see something like
variable'i'
. ↩