The Looseleaf Papers

OpenSCAD is not a good programming language

Created

Modified

Published

OpenSCAD is a wrapper around OpenCSG, a constructive solid geometry library. It works reasonably well for producing STL files from geometric primitives.

But the documentation also makes it sound as though OpenSCAD is a good programming language.

The variable retains its last assigned value at compile time, in line with Functional programming languages. Unlike Imperative languages, such as C, OpenSCAD is not an iterative language, as such the concept of x = x + 1 is not valid, get to understand this concept and you will understand the beauty of OpenSCAD.

https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language#Variables

I posit that OpenSCAD is a bad programming language, and is bad in ways that make it harder to use for CAD. Specifically:

  1. OpenSCAD has no user-defined error-handling such as exceptions or assertions.

    There isn’t even an exit() function.

    Not for lack of trying, though.

    http://forum.openscad.org/Would-be-nice-to-have-constraints-assertions-on-functions-modules-td11237.html

    Feature request for assert:

    https://github.com/openscad/openscad/issues/381

  2. OpenSCAD assigns variable values at compile-time, but does not warn if they are overwritten.

    This means that later assignments will silently overwrite previous values for the entire scope. For example, this code:

    a = 1; echo(a);
    a = 2; echo(a);
    a = 3; echo(a);
    

    will yield this output:

    ECHO: 3
    ECHO: 3
    ECHO: 3
    

    which is surprising behavior for a language that uses C-like procedural syntax and is otherwise evaluated top-to-bottom.

    In other words OpenSCAD variables are more like constants, but with an important difference. If variables are assigned a value multiple times, only the last assigned value is used in all places in the code.

    [ … ]

    This behavior is due to the need to supply variable input on the command line, via the use of -D variable=value option. OpenSCAD currently places that assignment at the end of the source code, and thus must allow a variables value to be changed for this purpose.

    https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Variables

    This is touted as a feature.

    While this appears to be counter-intuitive, it allows you to do some interesting things: For instance, if you set up your shared library files to have default values defined as variables at their root level, when you include that file in your own code, you can ‘re-define’ or override those constants by simply assigning a new value to them.

    https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/General#Scope_of_variables

    Interesting is not the same as desirable.

    There are other ways to handle this. For example, gnuplot allows overriding values from the command line, but does not use the same “last one wins” assignment model.

    https://stackoverflow.com/questions/12328603/how-to-pass-command-line-argument-to-gnuplot

    GNU make has an entire page on how to override variables.

    https://www.gnu.org/software/make/manual/html_node/Overriding.html

    This is a situation where “explicit is better than implicit”, since it is all too easy accidentally override them, which brings up the issue of namespaces.

  3. OpenSCAD has no namespaces.

    The include statement does not make a new namespace. Instead, it

    acts as if the contents of the included file were written in the including file

    https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Include_Statement

    This includes re-defining any global variables present in the included file, again with the “last assignment overrides any other values” rule.

  4. OpenSCAD has no vector index bounds checking.

    For example, it will happily compile this:

    cube([2.1, 3.1, 4,1]);
    

    as this:

    cube([2.1, 3.1, 4]);
    

    without so much as a warning.

    Similarly, these assignments:

    a = [1, 2, 3];
    b = a[3];
    c = a[-1];
    echo(a);
    echo(b);
    echo(c);
    

    will result in:

    ECHO: [1, 2, 3]
    ECHO: undef
    ECHO: undef
    

    instead of warning of a bounds violation.

    OpenSCAD doesn’t report errors, it silently ignores them. Good luck if you need to debug something.

    —Doug Moen

    https://github.com/openscad/openscad/issues/1574

  5. OpenSCAD lacks structs and enums.

    When working with 3D solids, structs would be useful for this sort of thing:

    diode.d = 9.10;
    diode.h = 6.04;
    diode.xyz = [24.48, 26.78, -0.6];
    translate(diode.xyz)
        cylinder(h=diode.h, d=diode.d, $fn=50);
    

    Unfortunately, there is no such mechanism in OpenSCAD. The only collection type is a vector, so the only way to access parts of a collection is with numeric indices.

    Feature request: Enums / Enumerations #1032

    https://github.com/openscad/openscad/issues/1032

  6. OpenSCAD requires semicolons to terminate statements.

    Scripting languages like Python, Ruby, and Lua use the end of line to terminate statements. Semicolons are only necessary when there are multiple statements per line.

    OpenSCAD, by contrast, requires semicolons to terminate every statement. If this provided better syntax errors, it might be justifiable. Unfortunately, this is not the case, because:

  7. OpenSCAD’s syntax errors are uninformative.

    For example, this code is missing a semicolon on line 1:

    a = 1
    echo(a);
    

    But OpenSCAD throws this error:

    Parser error in line 2: syntax error
    

    and highlights the parenthesis at column 5.

    An expression may span multiple lines, so this can make the actual error distant from the highlighted line.

    By contrast, attempting to compile this C code:

    #include <stdio.h>
    int main(int argc, char *argv[]) {
            int a = 1
            printf("%d\n", a);
            return 0;
    }
    

    results in this error from GCC:

    err.c: In function ‘main’:
    err.c:4:2: error: expected ‘,’ or ‘;’ before ‘printf’
      printf("%d\n", a);
      ^
    

    This is a trivial example, but is representative of OpenSCAD’s syntax error messages.

  8. OpenSCAD has no associative arrays.

    Use of the search() function can emulate this to some extent, but there is no data structure corresponding to a hash table.

And yes, people have forked it because of these sorts of problems:

OpenSCAD2 is a backwards compatible redesign of OpenSCAD. The main goals are expressive power and ease of use.

https://github.com/doug-moen/openscad2/blob/master/rfc/Overview.md

OpenSCAD is a bunch of C++ that makes CSG files, from which STL and similar files can be made. Why didn’t they use an existing embedded language, such as Lua or Guile?

http://emacstragic.net/programmatic-cad-openscad-alternatives/

In addition to being a poor programming language, OpenSCAD has deficiencies as a CAD tool:

  1. Centering cannot be adjusted by origin.

    Feature request: allow center= to accept more than a boolean

    https://github.com/openscad/openscad/issues/265

  2. OpenSCAD does not play nicely with other CAD formats.

    OpenSCAD claims to import STL, OFF, and DXF.

    In practice, it only correctly imports a narrow subset of those formats, and will frequently throw errors like “Unsupported DXF Entity” or silently do nothing.

    https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/FAQ#I.27m_getting_.22Unsupported_DXF_Entity.22_warnings_when_importing_DXF_files.2C_what_does_that_mean.3F

    http://forum.openscad.org/Importing-DXF-td3443.html

I would also argue that OpenSCAD’s commitment to a functional programming style is debatable. Here’s why:

  1. OpenSCAD has no function arity checking.

    The number of arguments to a function is not checked, neither in builtin functions and modules nor user-defined ones:

    cube(size=5, center=true, h=20);
    function myfunc(x) = 2*x+1; echo(myfunc(3,4,5));
    

    If this were to support functional constructs such as partial function application or currying, it might make some sense, but OpenSCAD does not support partial function application or currying, so this is just a source of bugs.

  2. OpenSCAD has no callback functions, lambda (anonymous) functions, or other higher-order functions.

    https://github.com/openscad/openscad/wiki/OEP3:-Function-Values-and-Variable-Scoping-improvements

    These are the sort of language constructs one would expect in a functional programming style.

  3. Special variables violate referential transparency.

    Special variables have dynamic scope, so they can be used to avoid explicitly passing a parameter. Many functional programming languages would use currying or closures instead of relying on dynamic scope.

Addendum 2016-10-10:

It provides a power set of primitive shapes and operations, but the language itself leaves a bit to be desired. This isn’t a beat-up-on-SCAD post, but a few of the things that irked me were:

  • Strange function application syntax (parameters in parens after the function name with an expression or block following the closing paren)
  • Unclear variable binding rules (multiple passes are made over the code and the results of changing a variable may affect things earlier in the code unexpectedly)
  • No package/namespace management
  • Multiple looping constructs that depend on what you are going to do with the results, not on how you want to loop

http://adereth.github.io/blog/2014/04/09/3d-printing-with-clojure/

Addendum 2020-03-12:

I love OpenSCAD for everything but the language, which is .. so bad that the rant stays corked.

https://news.ycombinator.com/item?id=22527535