Aug 11, 2018


  • administrators

    Jared Deckard
    I noticed function currying mentioned in the V2 brain storming doc. I am not using the CSG prototype methods since they are deprecated and I am starting to feel the awkwardness of nested operations. It would be nice if operations could take an option set without an object so that operations can be reused and manipulated as lists.

    move = translate([1, 2, 3])
    move(shape1)
    move(shape2)
    
    shape = do(
        translate([1, 2, 3]),
        rotate([0, 0, 45]),
        scale([3, 2, 1]),
        shape
    )
    

    I'm not sure if this is quite what you had in mind for currying, but I think it gets very close to the conciseness of OpenSCAD. I though I would gather some feedback and see if there is any existing progress before I start hacking on a prototype.
    4 comments
    4
    2 plus ones
    2
    no shares
    Shared publicly•View activity

    Jared Deckard
    +1
    Thanks for your insight. If you need any help fleshing the V2 roadmap out into issues or have some prerequisite issues that would be suitable for a "help wanted" tag, I am happy to help.
    
    Without a clear direction for working towards this feature in V2, I will probably implement this as a wrapper around V1 using lodash's curry and a custom pipeline function.
    

    Jared Deckard
    +1
    I have a prototype working:

    Plugin: https://github.com/deckar01/csg-curry/blob/master/index.js

    JSCAD integration: https://github.com/jscad/OpenJSCAD.org/compare/master...deckar01:csg-curry

    Lessons learned:

    • The spread (...) argument breaks lodash's curry implementation
    • Allowing a variable number of arguments via the "arguments" keyword breaks lodash's curry implementation
    • Some operations only accept one object
    • Some operations require an array instead of spread args
    • All the operations that accept spread args accept an array

    Hacks:

    • A custom curry function that ignores Function.length and waits to "close" the curry until it is called with no arguments
    • Injecting curried copies of all csg functions with a "$" prefix
    • Injecting a "$pipeline" function that normalizes multiple objects as a list, then applies curried functions like a stack

    Logo example:

    function main () {
      return $pipeline(
        $scale(10),
        $translate([0, 0, 1.5]),
        $union,
        difference(
          cube({size: 3, center: true}),
          sphere({r: 2, center: true})
        ),
        intersection(
          sphere({r: 1.3, center: true}),
          cube({size: 2.1, center: true})
        )
      )
    }
    

    Proposal:

    • Replace use of the "arguments" keyword with a required "options" dictionary argument
    • Replace spread arguments with a required object/array argument
    • Allow all operations to accept an array of objects
    • Curry the API with lodash/curry
    • Expose a "pipeline" function via the API

    Jared Deckard
    After iterating on the pipeline function a little bit, I no longer think currying is necessary to make a useful pipeline. Currying is elegant and familiar, but orthogonal to the goal of stacking operations without nesting.

    const pipeline = (...ops) => ops.reverse().reduce(
      (args, x) => x && x.call ? [x(...args)] : [x, ...args], []
    )
    
    function main () {
      return pipeline(
        scale, 10,
        translate, [0, 0, 1.5],
        union,
        difference(
          cube({size: 3, center: true}),
          sphere({r: 2, center: true})
        ),
        intersection(
          sphere({r: 1.3, center: true}),
          cube({size: 2.1, center: true})
        )
      )
    }
    

    Would you be interested in a PR that adds this functionality to the API in the master branch? No API change or new dependencies, just a 3 line pipeline utility. I am personally fine with copying this between my script since it is so compact, but I think it could really help clean up the complicated nesting most users will inevitably run into. I was constantly having to break operation chains off into variables at awkward points that were hard to name.

    Jeff Gay
    Jared, can you post the last comment to the issue, #114? We don’t want to loose your thoughts. And we can discuss the possibility of adding this to V2.