OpenSCAD User Manual/For C/Java/Python Programmers
OpenSCAD is a purely functional language. This implies a different style of programming compared to what you may be used to.
This document is written to prevent excessive frustration for programmers of (what they consider to be) "normal" programming languages, when learning OpenSCAD. Don't think you can skip it just because you're an expert programmer. In fact, the more experienced with procedural or object-oriented languages you are, the more this document can help you.
Imperative and procedural vs. declarative and functional
An imperative program consist of a series of actions that changes the program state. For instance, the statement
x = 5;
changes the value of x to 5 in most imperative languages.
x = square_root(x);
changes the value of x (from 5) into the square root of 5.
Imperative programs have a control flow — first this line, then the next line, until some construct such as if, for, while, or a function call changes the control flow.
A declarative language, on the other hand, lacks these attributes. Declarative languages include HTML, regular expressions, spreadsheets, Prolog, and purely functional languages. Declarative languages don't go through state changes (html code just is - there are no assignments in HTML). A declarative language is like a complex and formal description.
A subtype of declarative languages is "purely functional". It means that the same function called with the same parameters always return the same results. To guarantee that, no reading or writing to global variables or output can happen within functions.
OpenSCAD is a declarative, purely functional language. This is a source of confusion for many programmers used to imperative languages. They try to do "something simple" (like x = x + 1) in OpenSCAD and for some unexpected reason "it doesn't work" no matter how much of their expertise they throw at the problem. To prevent excessive frustration, read the rest of this document.
Benefits of a purely functional language
- Programs are more predictable, and thus less prone to bugs
- Easier to reason about programs, and prove them to be correct
- Easier to parallelize
- Shorter programs due to the high level language and concise syntax
- Particular to the way OpenSCAD models boolean operations, guarantees the mathematical properties of the boolean CSG operations (like the commutativity of union)
So now you know OpenSCAD was designed the way it is because it has certain benefits. Keep that in mind when you meet what may seem to be overly rigid limitations that keeps you from doing anything.
Two types of assignment
Both C and OpenSCAD uses the = symbol for assignment. Both languages differ in the exact meaning of "assignment". The C-style = is called a "destructive update operator" and reads "set .. to" (as in set myvar to 7). The operator is called a destructive because it destroys the value that was previously in the variable. It's called "update" because it updates the value of a variable from an old value to a new.
The OpenSCAD-style assignment reads "is". It is not destructive — you can't overwrite an old value with a new value. There is no old and new value, if x is 5, x is 5 in the entire scope, even before it was defined. This is closer to the mathematical definition of a variable and the equals sign.
It follows that reassignment (destructive update) of variables is not permitted in OpenSCAD. And there is no direct replacement. This means problems must be solved in a completely different way, using the following techniques:
- Different variable names
- Functions and recursion (using the ternary operator ? : instead of if)
- List comprehensions and the concat() function
// C cube(x); // undefined behavior - x not yet initialized x = 4; cube(x); // call a function to create cube of size 4 x = 5; // destructively update x to 5, in the process destroying the old value. cube(x); // call a function to create cube of size 5 x++; // set x to x+1. The increment operators in C are also destructive update operators. cube(x); // call a function to create cube of size 6
// OpenSCAD y = x + 1; // y is 6 cube(x); // declare a cube of size 5 x = 5; // x is 5. cube(x) // declare a cube of size 5 cube(y) // declare a cube of size 6 // The other lines are left out, because reassignment of variables are not permitted.
Unfortunately, reassignment currently doesn't give an error message, because it is used to specify overrides on the command line.