There is no way to compare text values for lexical ordering. In other words, you can't sort a list of strings.
The return/value statement can only ever appear at the end of a block of statements; there is no analog of the C/C++ return statement that terminates execution of the function in which it appears.
Assignments are a type of statement, but not a type of expression. (Easy to trip over if you're used to C/C++.)
if is a type of expression, but not a type of statement. (Another C/C++ gotcha.)
Statement blocks do not have side effects. In other words, assignments made within a statement block do not leave that block. For example:
foo = 1; bar = { foo = 2; // assignment local to statement block! value foo; }; // foo == 1 // bar == 2 |
People who are used to imperative languages find this quite confusing, particularly when using statement blocks as the then and else portions of if expressions. Adding to the confusion is the fact that iteration statements, which also contain a block of statements, do have side effects.
There is no straightforward way to iterate over a range of integers or the characters in a text value. However, either of these could be accomplished through a recursive function, for instance:
range_iterate(start : int, end : int, step : int, body : function(int)) : list { return if (start < end) then { result = <body(start)>; return result + range_iterate(start + step, end, step, body); } else <>; }; |
Function calls are a type of expression, but not a type of statement. The most obvious one with which this is an issue is _print. In order to call _print, you must do something with its return value, such as assign it to a variable:
_print("foo"); // This is a syntax error dummy = _print("foo"); // This prints "foo" as well as assigning "foo" to the variable dummy |
Function declaration and iteration are statements, and all statements end with a semicolon. In other words, you need a semicolon following the close brace ending a function or iteration block:
foreach [ n = v ] in binding do { ... }; // Without this semi-colon, you'll get a syntax error. myFunc(n : int, s : text) : text { ... }; // Same thing here: no semi-colon = syntax error. |
The Vesta SDL is a pure functional language. It's been dressed up to appear imperative (at least in some respects), but it has all the classic hallmarks of a functional language: no side effects, functions as first class values, etc. Once you realize the SDL is a functional language, a lot of things make more sense (for example, why if is an expression rather than a statement, and why you have to do something with the return value of _print).
Overlapping nested binding paths do not merge. It's possible to specify nested bindings by separating names with path delimiters, like this:
[ Cxx/switches/program/debug = "-g" ] // Equivalent to: [ Cxx = [ switches = [ program = [ debug = "-g" ] ] ] ] |
This is a very handy shortcut, and you'll see it used a lot. The problem is that it's very easy to write something like this:
[ Cxx/switches/program/debug = "-g", Cxx/switches/program/shared_libs = "-non_shared" ] |
If you have two paths in the same binding which overlap (as above), you'll get an evaluation error. Really what you want to do is use the recursive overlay operator, like this:
[ Cxx/switches/program/debug = "-g" ] ++ [ Cxx/switches/program/shared_libs = "-non_shared" ] |
Or only write the shared part of the path once, like this:
[ Cxx/switches/program = [ debug = "-g", shared_libs = "-non_shared" ] ] |
The result of the binding lookup operator cannot be used on the left-hand side of an assignment statement (i.e. is not an l-value). In other words, you can't do this:
b = [ x = 1 ]; y = 2; b/x = y; |
Instead you have to do somehting like this:
b += [ x = y ]; |
This often comes up when modifying the special "." variable:
./envVars/PATH = "/bin:/usr/bin:/usr/local/bin"; // WRONG! . ++= [ envVars/PATH = "/bin:/usr/bin:/usr/local/bin" ]; // Right |