Can you define "don't scale"? I keep hearing that but I feel like I've made relatively complex models with declarative DSLs and from a "writing code" perspective I do think they scale perfectly fine. I'll grant that performance gets a little bad, generally I end up having greatly reduce the face number when I'm working and bump it up when doing a final render.
This pattern repeats again and again[1] every time someone tries to push a DSL for complicated problems. DSLs are a great way to address the boilerplate and copying and tedium of working with bad frameworks in imperative languages. But they don't work once you really get rolling, and you end up in some kind of imperative hell anyway.
Declarative programming as an API design style is hugely valuable and great. Declarative programming languages as an implementation choice are IMHO a bad smell.
[1] If you think about it, this is really just another corrolary of Greenspun's Tenth Rule.
You don't like fluent APIs, which is your right. I think they have a place, and are a good DRY trick for grouping a bunch of things that would normally be "sequential" and making them look "declarative".
On the other hand I don't much like voodoo, and in my very limited experience with build123d it's sort of full of it. The very first "box" example looks like this:
with BuildPart() as ex1:
Box(length, width, thickness)
Why do we need a with expression here? What's the resource being allocated that must be freed? In particular why bother with "with" since the context appears to be implicit. The allocation gets bound to an "ex1" variable that is never used! How does Box() know where to put itself? It doesn't say, you just need to know what magic the toolkit is doing.I mean, is that fatal? No. I'm sure it's reasonably easy to explain. But everyone has tastes, and mine (informed by decades of scars from APIs like this) run hard toward the "make things explicit" side of the theater.
[1] Because if they didn't they wouldn't have much value. There are OpenCascade python bindings already, after all. But they don't match the use case of "write a simple script to emit your object" and are more aimed at "build a CAD tool".
In principle I think this means Build123D could even be given a FreeCAD workbench, but then there is no properly-supported CadQuery2 workbench at the moment (there's a fork that works but I don't think it has a real maintainer, and it's entirely possible that recent changes have broken it).
https://github.com/jpmlt/freecad-cadquery2-workbench
I definitely agree the implicit variable (in fact I don't think there is really an implicit variable, so much as dynamic scope detection, but we can imagine there is one) set up by the with statment is not particularly "Pythonic" but it is documented precisely here:
https://build123d.readthedocs.io/en/latest/key_concepts.html...
It's not ideal but it is still clearer than the CadQuery fluent API, which I don't really like, not because I think fluent APIs are bad at all (quite the opposite, actually) but because I don't believe the one in CadQuery makes a particularly good case for its existence (it's more the selector grammar I am unconvinced by).
* It's imperative python instead of a purely declarative custom language. Declarative DSLs are great when your problem is simple, but don't scale.
* The API design is sorta/kinda inspired by jQuery, operating on "selected sets" of subgeometry in a symmetric way, which recovers a lot of the "declarivity" for simple constructions.
* The underlying data model is a B-Rep thing built around OpenCascade. It has the same CSG primitives you're used to from OpenSCAD, but retains the ability to operate on faces and edges directly.
I'm no expert, but it's been a blast to play with. Definitely something to consider; OpenSCAD was sort of a mess back in the days of the RepRap Mendel, and frankly it hasn't improved much since.