7

Functional-Declarative languages should only be esoteric ones. They are interesting for research and a mathematical toy, but they should not be used for programming languages used in the real world.

I currently try to write OpenSCAD code that places a list of modules, with information given from an array, with varying sizes next to each other. And is so hard and cumbersome. Whoever had the idea to cripple OpenSCAD by not having variables was stupid or sadistic.

The actual CPU run instructions, one after the other, there is no good reason to not allow some imperative elements in a programming language.

Comments
  • 0
    I'm confused? As in the 3D modeling language?
  • 2
    It is a language designed to define geometric objects. Making it functional-declarative without mutation is the natural choice for the job.

    Obviously, some things are harder in such languages. And i never did anything in OpenSCAD. But you can use lists just fine in Erlang, which is functional without mutation too. So are you just missing a standard library with easy-to-use collection implementations (which definitely should be part of the language but is often lacking for the smaller ones powered by less devs)?
  • 2
    @Oktokolo I do feel for the OP, it takes a bit getting used to these sort of methods of programming, and a quick glance to the scripting language it has looks a bit strange. Still not as strange as Prolog, but strange enough.
  • 0
    @AleCx04 Me too. And honestly, i don't really get, for whom OpenSCAD actually is.

    But FreeCAD exists and supports Python scripting. And as a fallback there still is Blender, which can be convinced to create geometry via script (also in Python) too. The Blender API is pretty feature-complete and it supports automation of the GUI as well as direct definition of primitive geometry...
  • 0
    @PotatoCookie Yes. It is a turingcomplete language.

    I currently have a an array of objects. Each element gives the type of object (just an index). And there is a second list which describes how a type of object is build as a module (an actual 3D body).

    I want to place all the modules of the list next to each other. This works great when all modules have the same width. But as soon as the width is varies and the position of the next module depends on the position and width of the previous module, it becomes a pain to do in OpenSCAD.
  • 0
    @Oktokolo The problem is not that i can't use lists. The problem is that i can't use variables (only constants).
  • 1
    @happygimp0 Lists and recursion or iterator transformation in functional languages solve the same problems loops and variables solve in procedural languages. This sounds like it can be solved by consuming both lists with a recursive function and also passing down the cumulative width of all previous elements, but I don't know the tool so this is a blind guess.
  • 1
    When transforming loops to tail recursion, all mutable variables become arguments.
  • 0
    @lorentz Yes it is possible, it is just unnecessary complicated, inefficient, cumbersome, hard to think about and not how CPUs actually work. The other problem is that OpenSCAD has functions and modules. Functions can be recursive and return values, but they can't draw anything. Modules can draw stuff but can't be called recursive and can't return a value.

    So i need a loop and for every iteration, i need to call a tree of recursive calling functions that need to pass a index to each other to calculate the current position.
  • 0
    @happygimp0 Procedural languages don't work how CPUs work. Even Assembly doesn't work how CPUs work. If you actually wanted code that describes how CPUs work, each instruction would be a switching graph to describe information flow between the inputs and outputs of various components. I'd argue that this language would be better than Assembly because it would allow to skip a ton of exploitable speculation on the CPU. The performance of modern computers allows us to ship bytecode and compile it on the target anyway, so all that would happen is that the decoder moves from silicon into userspace and Assembly-like bytecode languages can evolve to better represent the concepts of the high level languages they're generated from.
  • 1
    @happygimp0 Either way, this computation is just

    - take n elements
    - map to width
    - reduce with addition

    Sounds more intuitive than "for all integer values from zero to n" for me
  • 0
    Sorry that's using a range, the true procedural approach would be "let i be 0, for as long as i is less than n, increment i and do the following"
  • 0
    Procedural is intuitive to you because it's what you've been using all your life. "No, actually i is a different number now" is a very unusual step for a lot of people, not to mention its consequences on shared values.
  • 0
    @lorentz The value has to change for every element, regardless of the approach (well, if modules could be called recursively, i could combine them and move them all, add another and combine them again and move all. That would avoid this but since it isn't possible in OpenSCAD we can forget about it). So you have to understand this concept.

    You already feed the CPU a stream of instructions, there is nothing else a CPU understands so bytecode is the closest thing you can do (regardless of any speculative execution, cache misses, address translation, ...) in that sense the CPU works procedural.

    I see what i have to

    - take n elements

    - map to width

    - reduce with addition

    But i need to do that for every element with all the previous elements. I see there is a lot of the same calculations that is done over and over and over again.
  • 0
    Compare this:

    xPosition=0

    for element in list:

    element.drawModule().translate([xPosition,0,0])

    x.Position+=element.getWidth(some,additional,parameters)

    Which is intuitive, you immediately see what xPosition is used for and how it is calculated. To this:

    function calculateXPosition(list,index,some,additional,parameters) =

    index>0 ? ( calculateXPosition(list,index)+getWidth(list[index],some,additional,parameters) ) : 0;

    for( index = [0:len(list)] )

    {

    xPosition=calculateXPosition(list,index,some,additional,parameters);

    translate([xPosition,0,0])

    drawElement(list[index]);

    }

    Where the CPU has to create a huge call stack for every element in the list, and you have to think about how that huge call stack behaves. Also, debugging it is a lot harder.
  • 0
    @happygimp0 I don't know your tool but my guess is that you can't recurse in the drawing parts because the language wants to ensure that drawing halts. If this is true, even if the non-drawing part would have procedural constructs, you'd still need two loops.
  • 0
    @happygimp0 Also if the environment that runs this code cares about efficiency, the functional executor is either lazy or does TCO, so no call stack is built.
  • 1
    @happygimp0 OpenSCAD has tail call optimization. This is Sparta! (and also functional). you don't have to worry about the stack when using tail calls for recursion.

    https://en.wikibooks.org/wiki/...
  • 0
    @Oktokolo Yes. But you have to first think about how this call stack would look like before you can create them.

    Why go from simple loop design in my head to complex call stack with recursive function, then write the recursive version, then read it again and think about what really happens only to then let OpenSCAD internally convert it back to a loop again?

    Wouldn't it be much simpler to go from simple loop in my head to simple loop in code to execute a simple loop?
  • 1
    @happygimp0 Nah, you don't have to think about call stacks. You want to traverse lists and carry a placement offset. So your function gets exactly that. And as long as the lists aren't fully processed, the last action of the function is calling itself with the remaining tails of the lists and the offset plus the consumed width. Forget the call stack, it is a tail recursive function. all you have to do is ensure that the lists get shorter with each call and that you terminate the recursion when the lists are empty.

    If you use a functional language, think functional. If you don't want to do that, just use Python in FreeCAD.
  • 0
    @happygimp0 I still don't get how repeatedly incrementing the value of a number that will eventually be used to index a list is more intuitive than recursion. You skip a lot of steps when describing a procedural loop, which are very much there, you just skip over them mentally because you're used to procedural programming.
  • 0
    @lorentz

    > I still don't get how repeatedly incrementing the value of a number that will eventually be used to index a list is more intuitive than recursion

    It is what is more close to the instructions the CPU actually runs. A CALL instruction (or whatever the name is on a architecture) has to put the current instruction pointer on the stack, increase or decrease the stack pointer (depending on the direction the stack grows), set the PC to a new value only to later use a RET instruction to revert all this. In comparison, incrementing a pointer is just adding a fixed value to a variable. Yes you can optimize it but why go farther away from the actual executed code when the one that is closer is easier to understand?

    > . You skip a lot of steps when describing a procedural loop, which are very much there

    No. That is just false. Converting a for-loop to assembly code is relatively straight forward (except some optimization).
  • 0
    @happygimp0 I'm saying that while it is closer to what the CPU does than recursion, a procedural loop is much further from the user's intent since it deals with indices instead of elements, and both of them are very far away from what really happens. None of those registers exist as a single isolated set of SRAM cells on the chip, and if CALL actually touched RAM half the time it's executed everything would grind to a halt. It's just another abstraction, like C with its infinite locals and allocator, and lambda calculus with its constant-time substitution.
  • 0
    I said you skip steps because when you talk about the procedural loop you keep ignoring the fact that it walks over numbers that just happen to index a list, and not the list itself.
Add Comment