discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Re: New feature in 2025.07.11: the object() function

CC
Cory Cross
Fri, Aug 22, 2025 8:17 PM

On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

This discussion is imho a perfect example of bikeshedding https://en.wiktionary.org/wiki/bikeshedding. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs

As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of struct_var.member it's l(struct_var,"member"). That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key.

  • Cory

... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies.

I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours.

I personally would appreciate a time box so we can make sure this does not get delayed another two years again.

Peter

On 21 Aug 2025, at 23:18, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org mailto:discuss@lists.openscad.org> wrote:

On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org mailto:discuss@lists.openscad.org> wrote:
Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy.

Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion.

We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler.

Following this logic, not adding it is simpler.

What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica.

So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer?

Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly.

"this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added.

Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ...

I am new here so I might not understand the mores in this project.

I am new as well (though a user for many years).

However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ...

I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here.

It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road.

I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right?

Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it.

The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023.

I also think it's important to keep momentum up.

I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-)

I can be a bit blunt but you can blame it on my Dutch ancestry :-).

  • Cory Cross

Peter

Actually, plists are better because you can get super methods :-)

I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors.

On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and object? Not good for my blood pressure! :-)

I like the builder approach and it is one of my drivers for the object work and $this.

BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ...

Peter Kriens

On 19 Aug 2025, at 07:47, Cory Cross via Discuss discuss@lists.openscad.org wrote:
On 8/16/25 8:49 AM, Jordan Brown wrote:

bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify();
I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all.  Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function?
If the former, I don't understand what it means.  If the latter, are you seriously suggesting this as a replacement for
threaded_nut(required, args, here, optional_generic_arc=its_value);
I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add using bosl2::threading to shorten names, at some point).

As a practical example, here is a invocation of a threaded module in my code:

buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK);

here's how I would do it with the builder pattern and the suggested OO approach:

buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify;

You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs:

buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK));

In this case, buttress_threaded_nut's implementation would change from 50 lines to 11:

module buttress_threaded_nut(kwargs) {
profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
];
generic_threaded_nut(struct(kwargs, profile=profile));
}

Of course, this isn't OO and makes it harder to detect argument name typos and such.

The builder pattern could be implemented as so:

// namespace for buttress_threaded_nut_builder
obj = struct(generic_threaded_nut_builder::new());
function new() =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
struct(obj)->profile(profile);

so not any more or less difficult to write. What do profile and positioning look like?

// namespace of generic_threaded_rod_builder
function profile(o is builder_obj, profile) =
assert(is_list(profile)) // And other tests independent of other values
struct(o,profile=profile);

// namespace of attachable_builder
function positioning(o is attachable_builder_obj, anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(o,l);

Okay, not in love with the repetition in there. But there are some improvements here:

  1. Some validation can now stop cluttering the top of so many functions/modules.
  2. The get_radius function doesn't need its args filled out every time
  3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature.
  4. We can match on the old types and convert to objects as needed

Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as object vs struct keywords.
Maybe attachable should be a mixin instead of in the class hierarchy.

So how would I write this with this as currently proposed?

function buttress_threaded_nut_builder =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */);

// in method list of generic_threaded_rod_builder
function set_profile(profile) =
assert(is_list(profile)) // And other tests independent of other values
object(this,profile=profile);

// in method list of attachable_builder
function set_positioning(anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(this,l);

module reify_buttress_threaded_nut(obj) {
reify_threaded_nut(obj);
}

module reify_threaded_nut(obj) {
reify_generic_threaded_nut(obj);
}

reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK));

...

Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be:

bosl2_threading = object(
top_level_constant = 27,
function buttress_threaded_nut_builder() =
...
);

module reify_buttress_threaded_nut(obj) {
bosl2_threading.top_level_constant; // for whatever reason
reify_threaded_nut(obj);
}

I've seen worse. I certainly can't say this rules out this.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org mailto:discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org mailto:discuss-leave@lists.openscad.org

On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >This discussion is imho a perfect example of bikeshedding <https://en.wiktionary.org/wiki/bikeshedding>. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of `struct_var.member` it's `l(struct_var,"member")`. That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key. - Cory > ... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies. > >I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours. > >I personally would appreciate a time box so we can make sure this does not get delayed another two years again. > > Peter > > >> On 21 Aug 2025, at 23:18, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >> >> >> >> On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org>> wrote: >>>> On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org>> wrote: >>> Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy. >> >> Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion. >> >>> We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler. >> >> Following this logic, not adding it is simpler. >> >> What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica. >> >>>> So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer? >>> >>> Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly. >> >> "this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added. >> >>> Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ... >>> >>> I am new here so I might not understand the mores in this project. >> >> I am new as well (though a user for many years). >> >>> However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ... >> >> I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here. >> >> It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road. >> >> I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right? >> >> Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it. >> >>> The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023. >> >> I also think it's important to keep momentum up. >> >>> I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-) >> >> I can be a bit blunt but you can blame it on my Dutch ancestry :-). >> >> - Cory Cross >> >>> >>> Peter >>> >>> >>> >>>> >>>> Actually, plists are better because you can get super methods :-) >>>> >>>> >>>> I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors. >>>> >>>> >>>> >>>> On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >>>>> You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and `object`? Not good for my blood pressure! :-) >>>>> >>>>> I like the builder approach and it is one of my drivers for the object work and $this. >>>>> >>>>> BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ... >>>>> >>>>> Peter Kriens >>>>> >>>>>> On 19 Aug 2025, at 07:47, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>>>>> On 8/16/25 8:49 AM, Jordan Brown wrote: >>>>>>>> bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify(); >>>>>>> I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all. Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function? >>>>>>> If the former, I don't understand what it means. If the latter, are you seriously suggesting this as a replacement for >>>>>>> threaded_nut(required, args, here, optional_generic_arc=its_value); >>>>>> I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add `using bosl2::threading` to shorten names, at some point). >>>>>> >>>>>> As a practical example, here is a invocation of a threaded module in my code: >>>>>> >>>>>> buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK); >>>>>> >>>>>> here's how I would do it with the builder pattern and the suggested OO approach: >>>>>> >>>>>> buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify; >>>>>> >>>>>> You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs: >>>>>> >>>>>> buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK)); >>>>>> >>>>>> In this case, buttress_threaded_nut's implementation would change from 50 lines to 11: >>>>>> >>>>>> module buttress_threaded_nut(kwargs) { >>>>>> profile = [ >>>>>> [ -1/2, -0.77], >>>>>> [ -7/16, -0.75], >>>>>> [ 5/16, 0], >>>>>> [ 7/16, 0], >>>>>> [ 7/16, -0.75], >>>>>> [ 1/ 2, -0.77], >>>>>> ]; >>>>>> generic_threaded_nut(struct(kwargs, profile=profile)); >>>>>> } >>>>>> >>>>>> Of course, this isn't OO and makes it harder to detect argument name typos and such. >>>>>> >>>>>> The builder pattern could be implemented as so: >>>>>> >>>>>> // namespace for buttress_threaded_nut_builder >>>>>> obj = struct(generic_threaded_nut_builder::new()); >>>>>> function new() = >>>>>> let ( profile = [ >>>>>> [ -1/2, -0.77], >>>>>> [ -7/16, -0.75], >>>>>> [ 5/16, 0], >>>>>> [ 7/16, 0], >>>>>> [ 7/16, -0.75], >>>>>> [ 1/ 2, -0.77], >>>>>> ]) >>>>>> struct(obj)->profile(profile); >>>>>> >>>>>> so not any more or less difficult to write. What do profile and positioning look like? >>>>>> >>>>>> // namespace of generic_threaded_rod_builder >>>>>> function profile(o is builder_obj, profile) = >>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>> struct(o,profile=profile); >>>>>> >>>>>> // namespace of attachable_builder >>>>>> function positioning(o is attachable_builder_obj, anchor, spin, orient) = >>>>>> assert(/* tests related to the parameters*/) >>>>>> let( >>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>> ) >>>>>> struct(o,l); >>>>>> >>>>>> Okay, not in love with the repetition in there. But there are some improvements here: >>>>>> >>>>>> 1. Some validation can now stop cluttering the top of so many functions/modules. >>>>>> 2. The `get_radius` function doesn't need its args filled out every time >>>>>> 3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature. >>>>>> 4. We can match on the old types and convert to objects as needed >>>>>> >>>>>> Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as `object` vs `struct` keywords. >>>>>> Maybe attachable should be a mixin instead of in the class hierarchy. >>>>>> >>>>>> So how would I write this with `this` as currently proposed? >>>>>> >>>>>> function buttress_threaded_nut_builder = >>>>>> let ( profile = [ >>>>>> [ -1/2, -0.77], >>>>>> [ -7/16, -0.75], >>>>>> [ 5/16, 0], >>>>>> [ 7/16, 0], >>>>>> [ 7/16, -0.75], >>>>>> [ 1/ 2, -0.77], >>>>>> ]) >>>>>> object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */); >>>>>> >>>>>> // in method list of generic_threaded_rod_builder >>>>>> function set_profile(profile) = >>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>> object(this,profile=profile); >>>>>> >>>>>> // in method list of attachable_builder >>>>>> function set_positioning(anchor, spin, orient) = >>>>>> assert(/* tests related to the parameters*/) >>>>>> let( >>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>> ) >>>>>> struct(this,l); >>>>>> >>>>>> module reify_buttress_threaded_nut(obj) { >>>>>> reify_threaded_nut(obj); >>>>>> } >>>>>> >>>>>> >>>>>> module reify_threaded_nut(obj) { >>>>>> reify_generic_threaded_nut(obj); >>>>>> } >>>>>> >>>>>> reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK)); >>>>>> >>>>>> >>>>>> ... >>>>>> >>>>>> Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be: >>>>>> >>>>>> bosl2_threading = object( >>>>>> top_level_constant = 27, >>>>>> function buttress_threaded_nut_builder() = >>>>>> ... >>>>>> ); >>>>>> >>>>>> module reify_buttress_threaded_nut(obj) { >>>>>> bosl2_threading.top_level_constant; // for whatever reason >>>>>> reify_threaded_nut(obj); >>>>>> } >>>>>> >>>>>> I've seen worse. I certainly can't say this rules out `this`. >>>>>> _______________________________________________ >>>>>> OpenSCAD mailing list >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>> >>>>> _______________________________________________ >>>>> OpenSCAD mailing list >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>> _______________________________________________ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org> >> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org>
V
vulcan_@mac.com
Fri, Aug 22, 2025 8:17 PM

sometimes i want my program (or scad script) to create and /or manipulate related data items with methods that are the only access to those items.

the requirement that methods be the interface into encapsulated data means that i need something more than an associative array (from my reading of assoc arrays at least, have i understood them correctly?)

But to make that work an object’s methods must be able to access its own elements, thus the need for $this, %%this%% or any other syntax that makes sense.

i don’t agree that using the word “object” implies that we must also have inheritance .. I am happy to use the word “object” for what has already been implemented .. even if $this is not added

sometimes i want my program (or scad script) to create and /or manipulate related data items with methods that are the **only** access to those items. the requirement that *methods* be the interface into encapsulated data means that i need something more than an associative array (from *my* reading of assoc arrays at least, have i understood them correctly?) But to make that work an object’s methods must be able to access its own elements, thus the need for $this, %%this%% or any other syntax that makes sense. i don’t agree that using the word “object” implies that we *must also* have inheritance .. I am happy to use the word “object” for what has already been implemented .. even if $this is not added
JB
Jon Bondy
Fri, Aug 22, 2025 8:31 PM

From

https://en.wikipedia.org/wiki/Object-oriented_programming

we find

*In the 1970s, the first version of the Smalltalk
https://en.wikipedia.org/wiki/Smalltalk programming language was
developed at Xerox PARC https://en.wikipedia.org/wiki/Xerox_PARC by
Alan Kay https://en.wikipedia.org/wiki/Alan_Kay, Dan Ingalls
https://en.wikipedia.org/wiki/Dan_Ingalls and Adele Goldberg
https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist).
Smalltalk-72 was notable for use of objects at the language level and
its graphical development environment.^[20]
https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-Bertrand_Meyer_2009_329-20
Smalltalk was a fully dynamic system, allowing users to create and
modify classes as they worked.^[21]
https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-21
Much of the theory of OOP was developed in the context of Smalltalk, for
example multiple inheritance.^[22]
https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-22 *

Having worked with these concepts for decades, Vulcan and I disagree on
this point.  I guess we all come from different backgrounds, and words
have different meanings for us.  But if we EVER want REAL objects in
OpenSCAD, then we need to call this initial attempt something different.

Jon

On 8/22/2025 4:17 PM, vulcan_--- via Discuss wrote:

i don’t agree that using the word “object” implies that we /must also/
have inheritance

--
This email has been checked for viruses by AVG antivirus software.
www.avg.com

From https://en.wikipedia.org/wiki/Object-oriented_programming we find *In the 1970s, the first version of the Smalltalk <https://en.wikipedia.org/wiki/Smalltalk> programming language was developed at Xerox PARC <https://en.wikipedia.org/wiki/Xerox_PARC> by Alan Kay <https://en.wikipedia.org/wiki/Alan_Kay>, Dan Ingalls <https://en.wikipedia.org/wiki/Dan_Ingalls> and Adele Goldberg <https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist)>. Smalltalk-72 was notable for use of objects at the language level and its graphical development environment.^[20] <https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-Bertrand_Meyer_2009_329-20> Smalltalk was a fully dynamic system, allowing users to create and modify classes as they worked.^[21] <https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-21> Much of the theory of OOP was developed in the context of Smalltalk, for example multiple inheritance.^[22] <https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-22> * Having worked with these concepts for decades, Vulcan and I disagree on this point.  I guess we all come from different backgrounds, and words have different meanings for us.  But if we EVER want REAL objects in OpenSCAD, then we need to call this initial attempt something different. Jon On 8/22/2025 4:17 PM, vulcan_--- via Discuss wrote: > > i don’t agree that using the word “object” implies that we /must also/ > have inheritance > > -- This email has been checked for viruses by AVG antivirus software. www.avg.com
HW
Harvey white
Fri, Aug 22, 2025 9:53 PM

The problem is not what "we" think it means, it's more what all the rest
of "them" are likely to think.

picking a name with few expectations may produce less assumptions.

Harvey

On 8/22/2025 4:31 PM, Jon Bondy via Discuss wrote:

From

https://en.wikipedia.org/wiki/Object-oriented_programming

we find

*In the 1970s, the first version of the Smalltalk
https://en.wikipedia.org/wiki/Smalltalk programming language was
developed at Xerox PARC https://en.wikipedia.org/wiki/Xerox_PARC by
Alan Kay https://en.wikipedia.org/wiki/Alan_Kay, Dan Ingalls
https://en.wikipedia.org/wiki/Dan_Ingalls and Adele Goldberg
https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist).
Smalltalk-72 was notable for use of objects at the language level and
its graphical development environment.^[20]
https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-Bertrand_Meyer_2009_329-20
Smalltalk was a fully dynamic system, allowing users to create and
modify classes as they worked.^[21]
https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-21
Much of the theory of OOP was developed in the context of Smalltalk,
for example multiple inheritance.^[22]
https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-22 *

Having worked with these concepts for decades, Vulcan and I disagree
on this point.  I guess we all come from different backgrounds, and
words have different meanings for us.  But if we EVER want REAL
objects in OpenSCAD, then we need to call this initial attempt
something different.

Jon

On 8/22/2025 4:17 PM, vulcan_--- via Discuss wrote:

i don’t agree that using the word “object” implies that we /must
also/ have inheritance

The problem is not what "we" think it means, it's more what all the rest of "them" are likely to think. picking a name with few expectations may produce less assumptions. Harvey On 8/22/2025 4:31 PM, Jon Bondy via Discuss wrote: > > From > > https://en.wikipedia.org/wiki/Object-oriented_programming > > we find > > *In the 1970s, the first version of the Smalltalk > <https://en.wikipedia.org/wiki/Smalltalk> programming language was > developed at Xerox PARC <https://en.wikipedia.org/wiki/Xerox_PARC> by > Alan Kay <https://en.wikipedia.org/wiki/Alan_Kay>, Dan Ingalls > <https://en.wikipedia.org/wiki/Dan_Ingalls> and Adele Goldberg > <https://en.wikipedia.org/wiki/Adele_Goldberg_(computer_scientist)>. > Smalltalk-72 was notable for use of objects at the language level and > its graphical development environment.^[20] > <https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-Bertrand_Meyer_2009_329-20> > Smalltalk was a fully dynamic system, allowing users to create and > modify classes as they worked.^[21] > <https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-21> > Much of the theory of OOP was developed in the context of Smalltalk, > for example multiple inheritance.^[22] > <https://en.wikipedia.org/wiki/Object-oriented_programming#cite_note-22> * > > Having worked with these concepts for decades, Vulcan and I disagree > on this point.  I guess we all come from different backgrounds, and > words have different meanings for us.  But if we EVER want REAL > objects in OpenSCAD, then we need to call this initial attempt > something different. > > Jon > > > On 8/22/2025 4:17 PM, vulcan_--- via Discuss wrote: >> >> i don’t agree that using the word “object” implies that we /must >> also/ have inheritance >> >> > > <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> > Virus-free.www.avg.com > <http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient> > > > <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> > > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org
AM
Adrian Mariano
Fri, Aug 22, 2025 10:41 PM

Yeah, you can implement structs as things stand.  I've done it.  And the
syntax struct_val(struct_name, "key_name") is horrible and the clunky
nature of the whole thing for practical use is such that I use them only as
a last resort.  I find a huge readability difference between code that
says thing.foo+thing.bar^2f(thing.baz-thing.other) as compared to
struct_val(thing,"foo")+struct_val(thing,"bar")^2
f(struct_val(thing,"baz")-struct_val(thing,"other")).
I'm sure also that performance is bad enough the userspace implementation
that it shouldn't be used any time performance matters.  I personally
don't care what the new feature is called.  Rename it map, dict or struct
if object offends people.

On Fri, Aug 22, 2025 at 4:18 PM Cory Cross via Discuss <
discuss@lists.openscad.org> wrote:

On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss <
discuss@lists.openscad.org> wrote:

This discussion is imho a perfect example of bikeshedding <

https://en.wiktionary.org/wiki/bikeshedding>. We can discuss this forever
on a forum like this because we're all highly experienced & opinionated but
in the mean time OpenSCAD has no KV pairs

As I pointed out before, this isn't true. If you want kv pairs it's
trivially implemented with alists. Instead of struct_var.member it's
l(struct_var,"member"). That's 17 vs 22 characters or 29% more for a
typical case. As a bonus, this userspace method is backwards-compatible and
forward-compatible with an optimized implementation which does also allow
struct_var.member syntax and allows any type as a key.

  • Cory

... The enemy of good is 'better' ... This is exactly how the OEP8

effort got derailed more than 2 years ago. At a certain moment everybody
gets confused and it dies.

I suggest you make a working PR so we can play with concrete proposals

instead of trying to infer what your sketches mean. If I can use KV pairs
and get some concise way to make methods I happily close my PR and support
yours.

I personally would appreciate a time box so we can make sure this does

not get delayed another two years again.

   Peter

On 21 Aug 2025, at 23:18, Cory Cross via Discuss <

On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <

On 21 Aug 2025, at 01:55, Cory Cross via Discuss <

Objects do NOT have parents so I am not sure what you're talking

about? An object is a flat set of key-value pairs. There is no hierarchy.

Then they shouldn't be called objects. At least 99% of people who have

or will use OpenSCAD will associate objects with the mainstream
object-oriented languages which all put inheritance front-and-center. It's
day-one "learning Python" material. I don't think there's a single language
with a "this" keyword that doesn't have inheritance. You're setting up
people for confusion.

We are very intentionally not implementing a full blow OO system to

keep OpenSCAD as simple as possible, but not simpler.

Following this logic, not adding it is simpler.

What do you want to be simple: writing SCAD or the implementation of

OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very
simple to implement. You can solve some very complicated analyses in a
single line of Mathematica.

So we already have the power to do builders, it's just slightly

uglier and slower. My question is while settle for "this" when generic
functions are even nicer?

Slightly??? We must live in another universe. There are few types of

code I'd like to write less than this kind of boiler plate code. I wrote
several of these 'OO' systems but they were quite ugly.

"this" refers to the proposal to add a "this" keyword. But what I wrote

shows that, with a couple helper functions, the proposed object() does not
result in substantially simpler code and by the maxim "keep OpenSCAD as
simple as possible, but not simpler", shouldn't be added.

Then you threw in an a-bom ... and another one. I am getting a bit

desperate and feel this is going way off the track and taking way too much
of my time ...

I am new here so I might not understand the mores in this project.

I am new as well (though a user for many years).

However, in other projects I am used that if you want to derail a PR

you make a fully working counter PR so people can play with the proposals
and compare. I find that you're now just dropping disruptive ideas ...

I've not found any other discussion of OEP8 and wasn't active at the

time anyway. I am discussing now because now is when I'm here.

It's my impression the "this" keyword is just being added because

people are unfamiliar with other systems. If SCAD was a hybrid
procedural/OO lisp with mutable values like JavaScript, then it'd be fine
to copy their semantics. But it's not and I think you're going down the
wrong road.

I'm trying to prove it by picking bosl2 and showing how I'd refactor it

using the proposed "this" approach or the one I'm proposing, because
ultimately what we want is what makes it easier and faster to write correct
code, right?

Everything I've proposed is quite easy to implement and I'll be happy

to do it and/or collaborate on it.

The danger here is that we spend a lot of time talking back and forth

and then nothing happens again because everything got so complicated. I
think this partly happened with OEP8 and that spent a lot of time in
discussion. There are very good, some crucial, ideas in that PR that has
been idling since 2023.

I also think it's important to keep momentum up.

I know I can be a bit blunt but you can blame it on my Dutch

citizenship ;-)

I can be a bit blunt but you can blame it on my Dutch ancestry :-).

  • Cory Cross
 Peter

Actually, plists are better because you can get super methods :-)

I'm on my phone composing this without Internet access, so please

forgive the formatting and mild syntax errors.

On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss <

You flabbergasted me with the completely different direction/syntax

you took but then at the end I saw that you came to the conclusion you
could do all this also with 'this' and object? Not good for my blood
pressure! :-)

I like the builder approach and it is one of my drivers for the

object work and $this.

BTW, notice that OEP8 also proposed to have modules as expression. I

am currently working on a PR for this. This will allow builders to also
call modules, have modules as variables, and hopefully modules as methods
when we can finally close [the $]this discussion ...

Peter Kriens

On 19 Aug 2025, at 07:47, Cory Cross via Discuss <

On 8/16/25 8:49 AM, Jordan Brown wrote:

bosl2::threading::nut_builder::new(required, args,

here)->optional_generic_arg(its_value)->reify();

I'm very sympathetic to the desire to reduce repetition in

argument handling, but I'm not understanding what that means at all.
Partly that's presentation; is this intended to be how the library would
say something, or how the caller would invoke the function?

If the former, I don't understand what it means.  If the latter,

are you seriously suggesting this as a replacement for

threaded_nut(required, args, here, optional_generic_arc=its_value);

I am suggesting it as a replacement for the latter; not because

it's better for the user, but because it's better for the maintainers and
not worse for the users. (I would assume we'd add using bosl2::threading
to shorten names, at some point).

As a practical example, here is a invocation of a threaded module

in my code:

buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK);

here's how I would do it with the builder pattern and the suggested

OO approach:

buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify;

You don't have to use the builder pattern. You could choose to use

objects as a replacement for Python's **kwargs:

buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK));

In this case, buttress_threaded_nut's implementation would change

from 50 lines to 11:

module buttress_threaded_nut(kwargs) {
profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
];
generic_threaded_nut(struct(kwargs, profile=profile));
}

Of course, this isn't OO and makes it harder to detect argument

name typos and such.

The builder pattern could be implemented as so:

// namespace for buttress_threaded_nut_builder
obj = struct(generic_threaded_nut_builder::new());
function new() =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
struct(obj)->profile(profile);

so not any more or less difficult to write. What do profile and

positioning look like?

// namespace of generic_threaded_rod_builder
function profile(o is builder_obj, profile) =
assert(is_list(profile)) // And other tests independent of other

values

struct(o,profile=profile);

// namespace of attachable_builder
function positioning(o is attachable_builder_obj, anchor, spin,

orient) =

assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(o,l);

Okay, not in love with the repetition in there. But there are some

improvements here:

  1. Some validation can now stop cluttering the top of so many

functions/modules.

  1. The get_radius function doesn't need its args filled out every

time

  1. We're reusing attachable instead of needing to redundantly pass

it so many args every single time and in every function and module
signature.

  1. We can match on the old types and convert to objects as needed

Unsolved issue: why would -> method invocation not look at the

namespace of positioning? I didn't intend it to, but it would do the wrong
thing as written; only the calls in the new() methods should add the
namespace to the method lookup. This might be best as object vs struct
keywords.

Maybe attachable should be a mixin instead of in the class

hierarchy.

So how would I write this with this as currently proposed?

function buttress_threaded_nut_builder =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
object(new_generic_threaded_rod_builder().set_profile(profile),

/* all methods on buttress_threaded_nut_builder must be defined here */);

// in method list of generic_threaded_rod_builder
function set_profile(profile) =
assert(is_list(profile)) // And other tests independent of other

values

object(this,profile=profile);

// in method list of attachable_builder
function set_positioning(anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(this,l);

module reify_buttress_threaded_nut(obj) {
reify_threaded_nut(obj);
}

module reify_threaded_nut(obj) {
reify_generic_threaded_nut(obj);
}

reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK));

...

Less different than I thought. Cascading explicit reify modules are

annoying. For this usage the inability to write your own generic methods
dispatching on other object types does not hinder anything. The shared
namespace means methods and values must have unique names. I think this is
supposed to solve name conflicts by having you only need one unique name
per file and you put all your constants in there? And methods, I guess, so
at the top of f.ex. bosl2/threading.scad there would be:

bosl2_threading = object(
top_level_constant = 27,
function buttress_threaded_nut_builder() =
...
);

module reify_buttress_threaded_nut(obj) {
bosl2_threading.top_level_constant; // for whatever reason
reify_threaded_nut(obj);
}

I've seen worse. I certainly can't say this rules out this.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

mailto:discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Yeah, you can implement structs as things stand. I've done it. And the syntax struct_val(struct_name, "key_name") is horrible and the clunky nature of the whole thing for practical use is such that I use them only as a last resort. I find a huge readability difference between code that says thing.foo+thing.bar^2*f(thing.baz-thing.other) as compared to struct_val(thing,"foo")+struct_val(thing,"bar")^2*f(struct_val(thing,"baz")-struct_val(thing,"other")). I'm sure also that performance is bad enough the userspace implementation that it shouldn't be used any time performance matters. I personally don't care what the new feature is called. Rename it map, dict or struct if object offends people. On Fri, Aug 22, 2025 at 4:18 PM Cory Cross via Discuss < discuss@lists.openscad.org> wrote: > > > On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss < > discuss@lists.openscad.org> wrote: > >This discussion is imho a perfect example of bikeshedding < > https://en.wiktionary.org/wiki/bikeshedding>. We can discuss this forever > on a forum like this because we're all highly experienced & opinionated but > in the mean time OpenSCAD has no KV pairs > > As I pointed out before, this isn't true. If you want kv pairs it's > trivially implemented with alists. Instead of `struct_var.member` it's > `l(struct_var,"member")`. That's 17 vs 22 characters or 29% more for a > typical case. As a bonus, this userspace method is backwards-compatible and > forward-compatible with an optimized implementation which does also allow > struct_var.member syntax and allows any type as a key. > > - Cory > > > > > ... The enemy of good is 'better' ... This is exactly how the OEP8 > effort got derailed more than 2 years ago. At a certain moment everybody > gets confused and it dies. > > > >I suggest you make a working PR so we can play with concrete proposals > instead of trying to infer what your sketches mean. If I can use KV pairs > and get some concise way to make methods I happily close my PR and support > yours. > > > >I personally would appreciate a time box so we can make sure this does > not get delayed another two years again. > > > > Peter > > > > > >> On 21 Aug 2025, at 23:18, Cory Cross via Discuss < > discuss@lists.openscad.org> wrote: > >> > >> > >> > >> On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss < > discuss@lists.openscad.org <mailto:discuss@lists.openscad.org>> wrote: > >>>> On 21 Aug 2025, at 01:55, Cory Cross via Discuss < > discuss@lists.openscad.org <mailto:discuss@lists.openscad.org>> wrote: > >>> Objects do NOT have parents so I am not sure what you're talking > about? An object is a flat set of key-value pairs. There is no hierarchy. > >> > >> Then they shouldn't be called objects. At least 99% of people who have > or will use OpenSCAD will associate objects with the mainstream > object-oriented languages which all put inheritance front-and-center. It's > day-one "learning Python" material. I don't think there's a single language > with a "this" keyword that doesn't have inheritance. You're setting up > people for confusion. > >> > >>> We are very intentionally not implementing a full blow OO system to > keep OpenSCAD as simple as possible, but not simpler. > >> > >> Following this logic, not adding it is simpler. > >> > >> What do you want to be simple: writing SCAD or the implementation of > OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very > simple to implement. You can solve some very complicated analyses in a > single line of Mathematica. > >> > >>>> So we already have the power to do builders, it's just slightly > uglier and slower. My question is while settle for "this" when generic > functions are even nicer? > >>> > >>> Slightly??? We must live in another universe. There are few types of > code I'd like to write less than this kind of boiler plate code. I wrote > several of these 'OO' systems but they were quite ugly. > >> > >> "this" refers to the proposal to add a "this" keyword. But what I wrote > shows that, with a couple helper functions, the proposed object() does not > result in substantially simpler code and by the maxim "keep OpenSCAD as > simple as possible, but not simpler", shouldn't be added. > >> > >>> Then you threw in an a-bom ... and another one. I am getting a bit > desperate and feel this is going way off the track and taking way too much > of my time ... > >>> > >>> I am new here so I might not understand the mores in this project. > >> > >> I am new as well (though a user for many years). > >> > >>> However, in other projects I am used that if you want to derail a PR > you make a fully working counter PR so people can play with the proposals > and compare. I find that you're now just dropping disruptive ideas ... > >> > >> I've not found any other discussion of OEP8 and wasn't active at the > time anyway. I am discussing now because now is when I'm here. > >> > >> It's my impression the "this" keyword is just being added because > people are unfamiliar with other systems. If SCAD was a hybrid > procedural/OO lisp with mutable values like JavaScript, then it'd be fine > to copy their semantics. But it's not and I think you're going down the > wrong road. > >> > >> I'm trying to prove it by picking bosl2 and showing how I'd refactor it > using the proposed "this" approach or the one I'm proposing, because > ultimately what we want is what makes it easier and faster to write correct > code, right? > >> > >> Everything I've proposed is quite easy to implement and I'll be happy > to do it and/or collaborate on it. > >> > >>> The danger here is that we spend a lot of time talking back and forth > and then nothing happens again because everything got so complicated. I > think this partly happened with OEP8 and that spent a lot of time in > discussion. There are very good, some crucial, ideas in that PR that has > been idling since 2023. > >> > >> I also think it's important to keep momentum up. > >> > >>> I know I can be a bit blunt but you can blame it on my Dutch > citizenship ;-) > >> > >> I can be a bit blunt but you can blame it on my Dutch ancestry :-). > >> > >> - Cory Cross > >> > >>> > >>> Peter > >>> > >>> > >>> > >>>> > >>>> Actually, plists are better because you can get super methods :-) > >>>> > >>>> > >>>> I'm on my phone composing this without Internet access, so please > forgive the formatting and mild syntax errors. > >>>> > >>>> > >>>> > >>>> On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss < > discuss@lists.openscad.org> wrote: > >>>>> You flabbergasted me with the completely different direction/syntax > you took but then at the end I saw that you came to the conclusion you > could do all this also with 'this' and `object`? Not good for my blood > pressure! :-) > >>>>> > >>>>> I like the builder approach and it is one of my drivers for the > object work and $this. > >>>>> > >>>>> BTW, notice that OEP8 also proposed to have modules as expression. I > am currently working on a PR for this. This will allow builders to also > call modules, have modules as variables, and hopefully modules as methods > when we can finally close [the $]this discussion ... > >>>>> > >>>>> Peter Kriens > >>>>> > >>>>>> On 19 Aug 2025, at 07:47, Cory Cross via Discuss < > discuss@lists.openscad.org> wrote: > >>>>>> On 8/16/25 8:49 AM, Jordan Brown wrote: > >>>>>>>> bosl2::threading::nut_builder::new(required, args, > here)->optional_generic_arg(its_value)->reify(); > >>>>>>> I'm very sympathetic to the desire to reduce repetition in > argument handling, but I'm not understanding what that means at all. > Partly that's presentation; is this intended to be how the library would > say something, or how the caller would invoke the function? > >>>>>>> If the former, I don't understand what it means. If the latter, > are you seriously suggesting this as a replacement for > >>>>>>> threaded_nut(required, args, here, optional_generic_arc=its_value); > >>>>>> I am suggesting it as a replacement for the latter; not because > it's better for the user, but because it's better for the maintainers and > not worse for the users. (I would assume we'd add `using bosl2::threading` > to shorten names, at some point). > >>>>>> > >>>>>> As a practical example, here is a invocation of a threaded module > in my code: > >>>>>> > >>>>>> > buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK); > >>>>>> > >>>>>> here's how I would do it with the builder pattern and the suggested > OO approach: > >>>>>> > >>>>>> > buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify; > >>>>>> > >>>>>> You don't have to use the builder pattern. You could choose to use > objects as a replacement for Python's **kwargs: > >>>>>> > >>>>>> > buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK)); > >>>>>> > >>>>>> In this case, buttress_threaded_nut's implementation would change > from 50 lines to 11: > >>>>>> > >>>>>> module buttress_threaded_nut(kwargs) { > >>>>>> profile = [ > >>>>>> [ -1/2, -0.77], > >>>>>> [ -7/16, -0.75], > >>>>>> [ 5/16, 0], > >>>>>> [ 7/16, 0], > >>>>>> [ 7/16, -0.75], > >>>>>> [ 1/ 2, -0.77], > >>>>>> ]; > >>>>>> generic_threaded_nut(struct(kwargs, profile=profile)); > >>>>>> } > >>>>>> > >>>>>> Of course, this isn't OO and makes it harder to detect argument > name typos and such. > >>>>>> > >>>>>> The builder pattern could be implemented as so: > >>>>>> > >>>>>> // namespace for buttress_threaded_nut_builder > >>>>>> obj = struct(generic_threaded_nut_builder::new()); > >>>>>> function new() = > >>>>>> let ( profile = [ > >>>>>> [ -1/2, -0.77], > >>>>>> [ -7/16, -0.75], > >>>>>> [ 5/16, 0], > >>>>>> [ 7/16, 0], > >>>>>> [ 7/16, -0.75], > >>>>>> [ 1/ 2, -0.77], > >>>>>> ]) > >>>>>> struct(obj)->profile(profile); > >>>>>> > >>>>>> so not any more or less difficult to write. What do profile and > positioning look like? > >>>>>> > >>>>>> // namespace of generic_threaded_rod_builder > >>>>>> function profile(o is builder_obj, profile) = > >>>>>> assert(is_list(profile)) // And other tests independent of other > values > >>>>>> struct(o,profile=profile); > >>>>>> > >>>>>> // namespace of attachable_builder > >>>>>> function positioning(o is attachable_builder_obj, anchor, spin, > orient) = > >>>>>> assert(/* tests related to the parameters*/) > >>>>>> let( > >>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], > >>>>>> is_undef(spin) ? [] : [["spin", spin]], > >>>>>> is_undef(orient) ? [] : [["orient", orient]]) > >>>>>> ) > >>>>>> struct(o,l); > >>>>>> > >>>>>> Okay, not in love with the repetition in there. But there are some > improvements here: > >>>>>> > >>>>>> 1. Some validation can now stop cluttering the top of so many > functions/modules. > >>>>>> 2. The `get_radius` function doesn't need its args filled out every > time > >>>>>> 3. We're reusing attachable instead of needing to redundantly pass > it so many args every single time and in every function and module > signature. > >>>>>> 4. We can match on the old types and convert to objects as needed > >>>>>> > >>>>>> Unsolved issue: why would -> method invocation not look at the > namespace of positioning? I didn't intend it to, but it would do the wrong > thing as written; only the calls in the new() methods should add the > namespace to the method lookup. This might be best as `object` vs `struct` > keywords. > >>>>>> Maybe attachable should be a mixin instead of in the class > hierarchy. > >>>>>> > >>>>>> So how would I write this with `this` as currently proposed? > >>>>>> > >>>>>> function buttress_threaded_nut_builder = > >>>>>> let ( profile = [ > >>>>>> [ -1/2, -0.77], > >>>>>> [ -7/16, -0.75], > >>>>>> [ 5/16, 0], > >>>>>> [ 7/16, 0], > >>>>>> [ 7/16, -0.75], > >>>>>> [ 1/ 2, -0.77], > >>>>>> ]) > >>>>>> object(new_generic_threaded_rod_builder().set_profile(profile), > /* all methods on buttress_threaded_nut_builder must be defined here */); > >>>>>> > >>>>>> // in method list of generic_threaded_rod_builder > >>>>>> function set_profile(profile) = > >>>>>> assert(is_list(profile)) // And other tests independent of other > values > >>>>>> object(this,profile=profile); > >>>>>> > >>>>>> // in method list of attachable_builder > >>>>>> function set_positioning(anchor, spin, orient) = > >>>>>> assert(/* tests related to the parameters*/) > >>>>>> let( > >>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], > >>>>>> is_undef(spin) ? [] : [["spin", spin]], > >>>>>> is_undef(orient) ? [] : [["orient", orient]]) > >>>>>> ) > >>>>>> struct(this,l); > >>>>>> > >>>>>> module reify_buttress_threaded_nut(obj) { > >>>>>> reify_threaded_nut(obj); > >>>>>> } > >>>>>> > >>>>>> > >>>>>> module reify_threaded_nut(obj) { > >>>>>> reify_generic_threaded_nut(obj); > >>>>>> } > >>>>>> > >>>>>> > reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK)); > >>>>>> > >>>>>> > >>>>>> ... > >>>>>> > >>>>>> Less different than I thought. Cascading explicit reify modules are > annoying. For this usage the inability to write your own generic methods > dispatching on other object types does not hinder anything. The shared > namespace means methods and values must have unique names. I think this is > supposed to solve name conflicts by having you only need one unique name > per file and you put all your constants in there? And methods, I guess, so > at the top of f.ex. bosl2/threading.scad there would be: > >>>>>> > >>>>>> bosl2_threading = object( > >>>>>> top_level_constant = 27, > >>>>>> function buttress_threaded_nut_builder() = > >>>>>> ... > >>>>>> ); > >>>>>> > >>>>>> module reify_buttress_threaded_nut(obj) { > >>>>>> bosl2_threading.top_level_constant; // for whatever reason > >>>>>> reify_threaded_nut(obj); > >>>>>> } > >>>>>> > >>>>>> I've seen worse. I certainly can't say this rules out `this`. > >>>>>> _______________________________________________ > >>>>>> OpenSCAD mailing list > >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org > >>>>> > >>>>> _______________________________________________ > >>>>> OpenSCAD mailing list > >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org > >>>> _______________________________________________ > >>>> OpenSCAD mailing list > >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org > >>> _______________________________________________ > >>> OpenSCAD mailing list > >>> To unsubscribe send an email to discuss-leave@lists.openscad.org > <mailto:discuss-leave@lists.openscad.org> > >> _______________________________________________ > >> OpenSCAD mailing list > >> To unsubscribe send an email to discuss-leave@lists.openscad.org > <mailto:discuss-leave@lists.openscad.org> > _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org >
CC
Cory Cross
Fri, Aug 22, 2025 11:58 PM

And, of course it's already done in BOSL2: https://github.com/BelfrySCAD/BOSL2/wiki/structs.scad

Throw in l=struct_val; to shorten the number of characters to type.

On August 22, 2025 4:17:31 PM EDT, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

This discussion is imho a perfect example of bikeshedding https://en.wiktionary.org/wiki/bikeshedding. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs

As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of struct_var.member it's l(struct_var,"member"). That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key.

  • Cory

... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies.

I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours.

I personally would appreciate a time box so we can make sure this does not get delayed another two years again.

Peter

On 21 Aug 2025, at 23:18, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org mailto:discuss@lists.openscad.org> wrote:

On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org mailto:discuss@lists.openscad.org> wrote:

Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy.

Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion.

We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler.

Following this logic, not adding it is simpler.

What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica.

So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer?

Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly.

"this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added.

Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ...

I am new here so I might not understand the mores in this project.

I am new as well (though a user for many years).

However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ...

I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here.

It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road.

I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right?

Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it.

The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023.

I also think it's important to keep momentum up.

I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-)

I can be a bit blunt but you can blame it on my Dutch ancestry :-).

  • Cory Cross
Peter

Actually, plists are better because you can get super methods :-)

I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors.

On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and object? Not good for my blood pressure! :-)

I like the builder approach and it is one of my drivers for the object work and $this.

BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ...

Peter Kriens

On 19 Aug 2025, at 07:47, Cory Cross via Discuss discuss@lists.openscad.org wrote:
On 8/16/25 8:49 AM, Jordan Brown wrote:

bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify();

I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all.  Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function?
If the former, I don't understand what it means.  If the latter, are you seriously suggesting this as a replacement for
threaded_nut(required, args, here, optional_generic_arc=its_value);

I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add using bosl2::threading to shorten names, at some point).

As a practical example, here is a invocation of a threaded module in my code:

buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK);

here's how I would do it with the builder pattern and the suggested OO approach:

buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify;

You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs:

buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK));

In this case, buttress_threaded_nut's implementation would change from 50 lines to 11:

module buttress_threaded_nut(kwargs) {
profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
];
generic_threaded_nut(struct(kwargs, profile=profile));
}

Of course, this isn't OO and makes it harder to detect argument name typos and such.

The builder pattern could be implemented as so:

// namespace for buttress_threaded_nut_builder
obj = struct(generic_threaded_nut_builder::new());
function new() =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
struct(obj)->profile(profile);

so not any more or less difficult to write. What do profile and positioning look like?

// namespace of generic_threaded_rod_builder
function profile(o is builder_obj, profile) =
assert(is_list(profile)) // And other tests independent of other values
struct(o,profile=profile);

// namespace of attachable_builder
function positioning(o is attachable_builder_obj, anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(o,l);

Okay, not in love with the repetition in there. But there are some improvements here:

  1. Some validation can now stop cluttering the top of so many functions/modules.
  2. The get_radius function doesn't need its args filled out every time
  3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature.
  4. We can match on the old types and convert to objects as needed

Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as object vs struct keywords.
Maybe attachable should be a mixin instead of in the class hierarchy.

So how would I write this with this as currently proposed?

function buttress_threaded_nut_builder =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */);

// in method list of generic_threaded_rod_builder
function set_profile(profile) =
assert(is_list(profile)) // And other tests independent of other values
object(this,profile=profile);

// in method list of attachable_builder
function set_positioning(anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(this,l);

module reify_buttress_threaded_nut(obj) {
reify_threaded_nut(obj);
}

module reify_threaded_nut(obj) {
reify_generic_threaded_nut(obj);
}

reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK));

...

Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be:

bosl2_threading = object(
top_level_constant = 27,
function buttress_threaded_nut_builder() =
...
);

module reify_buttress_threaded_nut(obj) {
bosl2_threading.top_level_constant; // for whatever reason
reify_threaded_nut(obj);
}

I've seen worse. I certainly can't say this rules out this.


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org mailto:discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org mailto:discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

And, of course it's already done in BOSL2: https://github.com/BelfrySCAD/BOSL2/wiki/structs.scad Throw in `l=struct_val;` to shorten the number of characters to type. On August 22, 2025 4:17:31 PM EDT, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: > > >On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >>This discussion is imho a perfect example of bikeshedding <https://en.wiktionary.org/wiki/bikeshedding>. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs > >As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of `struct_var.member` it's `l(struct_var,"member")`. That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key. > >- Cory > > > >> ... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies. >> >>I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours. >> >>I personally would appreciate a time box so we can make sure this does not get delayed another two years again. >> >> Peter >> >> >>> On 21 Aug 2025, at 23:18, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>> >>> >>> >>> On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org>> wrote: >>>>> On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org>> wrote: >>>> Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy. >>> >>> Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion. >>> >>>> We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler. >>> >>> Following this logic, not adding it is simpler. >>> >>> What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica. >>> >>>>> So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer? >>>> >>>> Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly. >>> >>> "this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added. >>> >>>> Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ... >>>> >>>> I am new here so I might not understand the mores in this project. >>> >>> I am new as well (though a user for many years). >>> >>>> However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ... >>> >>> I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here. >>> >>> It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road. >>> >>> I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right? >>> >>> Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it. >>> >>>> The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023. >>> >>> I also think it's important to keep momentum up. >>> >>>> I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-) >>> >>> I can be a bit blunt but you can blame it on my Dutch ancestry :-). >>> >>> - Cory Cross >>> >>>> >>>> Peter >>>> >>>> >>>> >>>>> >>>>> Actually, plists are better because you can get super methods :-) >>>>> >>>>> >>>>> I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors. >>>>> >>>>> >>>>> >>>>> On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >>>>>> You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and `object`? Not good for my blood pressure! :-) >>>>>> >>>>>> I like the builder approach and it is one of my drivers for the object work and $this. >>>>>> >>>>>> BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ... >>>>>> >>>>>> Peter Kriens >>>>>> >>>>>>> On 19 Aug 2025, at 07:47, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>>>>>> On 8/16/25 8:49 AM, Jordan Brown wrote: >>>>>>>>> bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify(); >>>>>>>> I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all. Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function? >>>>>>>> If the former, I don't understand what it means. If the latter, are you seriously suggesting this as a replacement for >>>>>>>> threaded_nut(required, args, here, optional_generic_arc=its_value); >>>>>>> I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add `using bosl2::threading` to shorten names, at some point). >>>>>>> >>>>>>> As a practical example, here is a invocation of a threaded module in my code: >>>>>>> >>>>>>> buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK); >>>>>>> >>>>>>> here's how I would do it with the builder pattern and the suggested OO approach: >>>>>>> >>>>>>> buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify; >>>>>>> >>>>>>> You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs: >>>>>>> >>>>>>> buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK)); >>>>>>> >>>>>>> In this case, buttress_threaded_nut's implementation would change from 50 lines to 11: >>>>>>> >>>>>>> module buttress_threaded_nut(kwargs) { >>>>>>> profile = [ >>>>>>> [ -1/2, -0.77], >>>>>>> [ -7/16, -0.75], >>>>>>> [ 5/16, 0], >>>>>>> [ 7/16, 0], >>>>>>> [ 7/16, -0.75], >>>>>>> [ 1/ 2, -0.77], >>>>>>> ]; >>>>>>> generic_threaded_nut(struct(kwargs, profile=profile)); >>>>>>> } >>>>>>> >>>>>>> Of course, this isn't OO and makes it harder to detect argument name typos and such. >>>>>>> >>>>>>> The builder pattern could be implemented as so: >>>>>>> >>>>>>> // namespace for buttress_threaded_nut_builder >>>>>>> obj = struct(generic_threaded_nut_builder::new()); >>>>>>> function new() = >>>>>>> let ( profile = [ >>>>>>> [ -1/2, -0.77], >>>>>>> [ -7/16, -0.75], >>>>>>> [ 5/16, 0], >>>>>>> [ 7/16, 0], >>>>>>> [ 7/16, -0.75], >>>>>>> [ 1/ 2, -0.77], >>>>>>> ]) >>>>>>> struct(obj)->profile(profile); >>>>>>> >>>>>>> so not any more or less difficult to write. What do profile and positioning look like? >>>>>>> >>>>>>> // namespace of generic_threaded_rod_builder >>>>>>> function profile(o is builder_obj, profile) = >>>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>>> struct(o,profile=profile); >>>>>>> >>>>>>> // namespace of attachable_builder >>>>>>> function positioning(o is attachable_builder_obj, anchor, spin, orient) = >>>>>>> assert(/* tests related to the parameters*/) >>>>>>> let( >>>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>>> ) >>>>>>> struct(o,l); >>>>>>> >>>>>>> Okay, not in love with the repetition in there. But there are some improvements here: >>>>>>> >>>>>>> 1. Some validation can now stop cluttering the top of so many functions/modules. >>>>>>> 2. The `get_radius` function doesn't need its args filled out every time >>>>>>> 3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature. >>>>>>> 4. We can match on the old types and convert to objects as needed >>>>>>> >>>>>>> Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as `object` vs `struct` keywords. >>>>>>> Maybe attachable should be a mixin instead of in the class hierarchy. >>>>>>> >>>>>>> So how would I write this with `this` as currently proposed? >>>>>>> >>>>>>> function buttress_threaded_nut_builder = >>>>>>> let ( profile = [ >>>>>>> [ -1/2, -0.77], >>>>>>> [ -7/16, -0.75], >>>>>>> [ 5/16, 0], >>>>>>> [ 7/16, 0], >>>>>>> [ 7/16, -0.75], >>>>>>> [ 1/ 2, -0.77], >>>>>>> ]) >>>>>>> object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */); >>>>>>> >>>>>>> // in method list of generic_threaded_rod_builder >>>>>>> function set_profile(profile) = >>>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>>> object(this,profile=profile); >>>>>>> >>>>>>> // in method list of attachable_builder >>>>>>> function set_positioning(anchor, spin, orient) = >>>>>>> assert(/* tests related to the parameters*/) >>>>>>> let( >>>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>>> ) >>>>>>> struct(this,l); >>>>>>> >>>>>>> module reify_buttress_threaded_nut(obj) { >>>>>>> reify_threaded_nut(obj); >>>>>>> } >>>>>>> >>>>>>> >>>>>>> module reify_threaded_nut(obj) { >>>>>>> reify_generic_threaded_nut(obj); >>>>>>> } >>>>>>> >>>>>>> reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK)); >>>>>>> >>>>>>> >>>>>>> ... >>>>>>> >>>>>>> Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be: >>>>>>> >>>>>>> bosl2_threading = object( >>>>>>> top_level_constant = 27, >>>>>>> function buttress_threaded_nut_builder() = >>>>>>> ... >>>>>>> ); >>>>>>> >>>>>>> module reify_buttress_threaded_nut(obj) { >>>>>>> bosl2_threading.top_level_constant; // for whatever reason >>>>>>> reify_threaded_nut(obj); >>>>>>> } >>>>>>> >>>>>>> I've seen worse. I certainly can't say this rules out `this`. >>>>>>> _______________________________________________ >>>>>>> OpenSCAD mailing list >>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>> >>>>>> _______________________________________________ >>>>>> OpenSCAD mailing list >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>> _______________________________________________ >>>>> OpenSCAD mailing list >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>> _______________________________________________ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org> >>> _______________________________________________ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org> >_______________________________________________ >OpenSCAD mailing list >To unsubscribe send an email to discuss-leave@lists.openscad.org
AM
Adrian Mariano
Sat, Aug 23, 2025 12:06 AM

Perhaps you're not aware that I'm one of the BOSL2 authors, so when I say
"I implemented" that means it's in BOSL2.  Changing struct_val to l just
makes it more confusing to read because the function calls are now very
short. It's not about character count exactly---I type very fast---but
about syntactic simplicity and clarity---code readability.  Basically any
time I use these I end up writing foo = struct_val(thing,"foo") to avoid
the syntactic mess that arises from direct access to the structure.  (I am
not a fan of python dicts where you need quotes either.  I return
namedtuples from my functions instead of dicts.)

On Fri, Aug 22, 2025 at 7:58 PM Cory Cross via Discuss <
discuss@lists.openscad.org> wrote:

And, of course it's already done in BOSL2:
https://github.com/BelfrySCAD/BOSL2/wiki/structs.scad

Throw in l=struct_val; to shorten the number of characters to type.

On August 22, 2025 4:17:31 PM EDT, Cory Cross via Discuss <
discuss@lists.openscad.org> wrote:

On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

This discussion is imho a perfect example of bikeshedding https://en.wiktionary.org/wiki/bikeshedding. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs

As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of struct_var.member it's l(struct_var,"member"). That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key.

  • Cory

... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies.

I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours.

I personally would appreciate a time box so we can make sure this does not get delayed another two years again.

Peter

On 21 Aug 2025, at 23:18, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org discuss@lists.openscad.org>> wrote:

On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org discuss@lists.openscad.org>> wrote:

Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy.

Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion.

We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler.

Following this logic, not adding it is simpler.

What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica.

So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer?

Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly.

"this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added.

Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ...

I am new here so I might not understand the mores in this project.

I am new as well (though a user for many years).

However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ...

I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here.

It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road.

I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right?

Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it.

The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023.

I also think it's important to keep momentum up.

I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-)

I can be a bit blunt but you can blame it on my Dutch ancestry :-).

  • Cory Cross
Peter

Actually, plists are better because you can get super methods :-)

I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors.

On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and object? Not good for my blood pressure! :-)

I like the builder approach and it is one of my drivers for the object work and $this.

BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ...

Peter Kriens

On 19 Aug 2025, at 07:47, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On 8/16/25 8:49 AM, Jordan Brown wrote:

bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify();

I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all.  Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function?
If the former, I don't understand what it means.  If the latter, are you seriously suggesting this as a replacement for
threaded_nut(required, args, here, optional_generic_arc=its_value);

I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add using bosl2::threading to shorten names, at some point).

As a practical example, here is a invocation of a threaded module in my code:

buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK);

here's how I would do it with the builder pattern and the suggested OO approach:

buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify;

You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs:

buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK));

In this case, buttress_threaded_nut's implementation would change from 50 lines to 11:

module buttress_threaded_nut(kwargs) {
profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
];
generic_threaded_nut(struct(kwargs, profile=profile));
}

Of course, this isn't OO and makes it harder to detect argument name typos and such.

The builder pattern could be implemented as so:

// namespace for buttress_threaded_nut_builder
obj = struct(generic_threaded_nut_builder::new());
function new() =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
struct(obj)->profile(profile);

so not any more or less difficult to write. What do profile and positioning look like?

// namespace of generic_threaded_rod_builder
function profile(o is builder_obj, profile) =
assert(is_list(profile)) // And other tests independent of other values
struct(o,profile=profile);

// namespace of attachable_builder
function positioning(o is attachable_builder_obj, anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(o,l);

Okay, not in love with the repetition in there. But there are some improvements here:

  1. Some validation can now stop cluttering the top of so many functions/modules.
  2. The get_radius function doesn't need its args filled out every time
  3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature.
  4. We can match on the old types and convert to objects as needed

Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as object vs struct keywords.
Maybe attachable should be a mixin instead of in the class hierarchy.

So how would I write this with this as currently proposed?

function buttress_threaded_nut_builder =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */);

// in method list of generic_threaded_rod_builder
function set_profile(profile) =
assert(is_list(profile)) // And other tests independent of other values
object(this,profile=profile);

// in method list of attachable_builder
function set_positioning(anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(this,l);

module reify_buttress_threaded_nut(obj) {
reify_threaded_nut(obj);
}

module reify_threaded_nut(obj) {
reify_generic_threaded_nut(obj);
}

reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK));

...

Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be:

bosl2_threading = object(
top_level_constant = 27,
function buttress_threaded_nut_builder() =
...
);

module reify_buttress_threaded_nut(obj) {
bosl2_threading.top_level_constant; // for whatever reason
reify_threaded_nut(obj);
}

I've seen worse. I certainly can't say this rules out this.

OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org discuss-leave@lists.openscad.org>


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org discuss-leave@lists.openscad.org>


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

Perhaps you're not aware that I'm one of the BOSL2 authors, so when I say "I implemented" that means it's in BOSL2. Changing struct_val to l just makes it more confusing to read because the function calls are now very short. It's not about character count exactly---I type very fast---but about syntactic simplicity and clarity---code readability. Basically any time I use these I end up writing foo = struct_val(thing,"foo") to avoid the syntactic mess that arises from direct access to the structure. (I am not a fan of python dicts where you need quotes either. I return namedtuples from my functions instead of dicts.) On Fri, Aug 22, 2025 at 7:58 PM Cory Cross via Discuss < discuss@lists.openscad.org> wrote: > And, of course it's already done in BOSL2: > https://github.com/BelfrySCAD/BOSL2/wiki/structs.scad > > Throw in `l=struct_val;` to shorten the number of characters to type. > > > On August 22, 2025 4:17:31 PM EDT, Cory Cross via Discuss < > discuss@lists.openscad.org> wrote: > >> >> >> On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >> >>> This discussion is imho a perfect example of bikeshedding <https://en.wiktionary.org/wiki/bikeshedding>. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs >>> >> >> As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of `struct_var.member` it's `l(struct_var,"member")`. That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key. >> >> - Cory >> >> >> >> ... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies. >>> >>> I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours. >>> >>> I personally would appreciate a time box so we can make sure this does not get delayed another two years again. >>> >>> Peter >>> >>> >>> On 21 Aug 2025, at 23:18, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>>> >>>> >>>> >>>> On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org <discuss@lists.openscad.org>>> wrote: >>>> >>>>> On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org <discuss@lists.openscad.org>>> wrote: >>>>>> >>>>> Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy. >>>>> >>>> >>>> Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion. >>>> >>>> We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler. >>>>> >>>> >>>> Following this logic, not adding it is simpler. >>>> >>>> What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica. >>>> >>>> So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer? >>>>>> >>>>> >>>>> Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly. >>>>> >>>> >>>> "this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added. >>>> >>>> Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ... >>>>> >>>>> I am new here so I might not understand the mores in this project. >>>>> >>>> >>>> I am new as well (though a user for many years). >>>> >>>> However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ... >>>>> >>>> >>>> I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here. >>>> >>>> It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road. >>>> >>>> I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right? >>>> >>>> Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it. >>>> >>>> The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023. >>>>> >>>> >>>> I also think it's important to keep momentum up. >>>> >>>> I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-) >>>>> >>>> >>>> I can be a bit blunt but you can blame it on my Dutch ancestry :-). >>>> >>>> - Cory Cross >>>> >>>> >>>>> Peter >>>>> >>>>> >>>>> >>>>> >>>>>> Actually, plists are better because you can get super methods :-) >>>>>> >>>>>> >>>>>> I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors. >>>>>> >>>>>> >>>>>> >>>>>> On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >>>>>> >>>>>>> You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and `object`? Not good for my blood pressure! :-) >>>>>>> >>>>>>> I like the builder approach and it is one of my drivers for the object work and $this. >>>>>>> >>>>>>> BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ... >>>>>>> >>>>>>> Peter Kriens >>>>>>> >>>>>>> On 19 Aug 2025, at 07:47, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>>>>>>> On 8/16/25 8:49 AM, Jordan Brown wrote: >>>>>>>> >>>>>>>>> bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify(); >>>>>>>>>> >>>>>>>>> I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all. Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function? >>>>>>>>> If the former, I don't understand what it means. If the latter, are you seriously suggesting this as a replacement for >>>>>>>>> threaded_nut(required, args, here, optional_generic_arc=its_value); >>>>>>>>> >>>>>>>> I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add `using bosl2::threading` to shorten names, at some point). >>>>>>>> >>>>>>>> As a practical example, here is a invocation of a threaded module in my code: >>>>>>>> >>>>>>>> buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK); >>>>>>>> >>>>>>>> here's how I would do it with the builder pattern and the suggested OO approach: >>>>>>>> >>>>>>>> buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify; >>>>>>>> >>>>>>>> You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs: >>>>>>>> >>>>>>>> buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK)); >>>>>>>> >>>>>>>> In this case, buttress_threaded_nut's implementation would change from 50 lines to 11: >>>>>>>> >>>>>>>> module buttress_threaded_nut(kwargs) { >>>>>>>> profile = [ >>>>>>>> [ -1/2, -0.77], >>>>>>>> [ -7/16, -0.75], >>>>>>>> [ 5/16, 0], >>>>>>>> [ 7/16, 0], >>>>>>>> [ 7/16, -0.75], >>>>>>>> [ 1/ 2, -0.77], >>>>>>>> ]; >>>>>>>> generic_threaded_nut(struct(kwargs, profile=profile)); >>>>>>>> } >>>>>>>> >>>>>>>> Of course, this isn't OO and makes it harder to detect argument name typos and such. >>>>>>>> >>>>>>>> The builder pattern could be implemented as so: >>>>>>>> >>>>>>>> // namespace for buttress_threaded_nut_builder >>>>>>>> obj = struct(generic_threaded_nut_builder::new()); >>>>>>>> function new() = >>>>>>>> let ( profile = [ >>>>>>>> [ -1/2, -0.77], >>>>>>>> [ -7/16, -0.75], >>>>>>>> [ 5/16, 0], >>>>>>>> [ 7/16, 0], >>>>>>>> [ 7/16, -0.75], >>>>>>>> [ 1/ 2, -0.77], >>>>>>>> ]) >>>>>>>> struct(obj)->profile(profile); >>>>>>>> >>>>>>>> so not any more or less difficult to write. What do profile and positioning look like? >>>>>>>> >>>>>>>> // namespace of generic_threaded_rod_builder >>>>>>>> function profile(o is builder_obj, profile) = >>>>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>>>> struct(o,profile=profile); >>>>>>>> >>>>>>>> // namespace of attachable_builder >>>>>>>> function positioning(o is attachable_builder_obj, anchor, spin, orient) = >>>>>>>> assert(/* tests related to the parameters*/) >>>>>>>> let( >>>>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>>>> ) >>>>>>>> struct(o,l); >>>>>>>> >>>>>>>> Okay, not in love with the repetition in there. But there are some improvements here: >>>>>>>> >>>>>>>> 1. Some validation can now stop cluttering the top of so many functions/modules. >>>>>>>> 2. The `get_radius` function doesn't need its args filled out every time >>>>>>>> 3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature. >>>>>>>> 4. We can match on the old types and convert to objects as needed >>>>>>>> >>>>>>>> Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as `object` vs `struct` keywords. >>>>>>>> Maybe attachable should be a mixin instead of in the class hierarchy. >>>>>>>> >>>>>>>> So how would I write this with `this` as currently proposed? >>>>>>>> >>>>>>>> function buttress_threaded_nut_builder = >>>>>>>> let ( profile = [ >>>>>>>> [ -1/2, -0.77], >>>>>>>> [ -7/16, -0.75], >>>>>>>> [ 5/16, 0], >>>>>>>> [ 7/16, 0], >>>>>>>> [ 7/16, -0.75], >>>>>>>> [ 1/ 2, -0.77], >>>>>>>> ]) >>>>>>>> object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */); >>>>>>>> >>>>>>>> // in method list of generic_threaded_rod_builder >>>>>>>> function set_profile(profile) = >>>>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>>>> object(this,profile=profile); >>>>>>>> >>>>>>>> // in method list of attachable_builder >>>>>>>> function set_positioning(anchor, spin, orient) = >>>>>>>> assert(/* tests related to the parameters*/) >>>>>>>> let( >>>>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>>>> ) >>>>>>>> struct(this,l); >>>>>>>> >>>>>>>> module reify_buttress_threaded_nut(obj) { >>>>>>>> reify_threaded_nut(obj); >>>>>>>> } >>>>>>>> >>>>>>>> >>>>>>>> module reify_threaded_nut(obj) { >>>>>>>> reify_generic_threaded_nut(obj); >>>>>>>> } >>>>>>>> >>>>>>>> reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK)); >>>>>>>> >>>>>>>> >>>>>>>> ... >>>>>>>> >>>>>>>> Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be: >>>>>>>> >>>>>>>> bosl2_threading = object( >>>>>>>> top_level_constant = 27, >>>>>>>> function buttress_threaded_nut_builder() = >>>>>>>> ... >>>>>>>> ); >>>>>>>> >>>>>>>> module reify_buttress_threaded_nut(obj) { >>>>>>>> bosl2_threading.top_level_constant; // for whatever reason >>>>>>>> reify_threaded_nut(obj); >>>>>>>> } >>>>>>>> >>>>>>>> I've seen worse. I certainly can't say this rules out `this`. >>>>>>>> ------------------------------ >>>>>>>> OpenSCAD mailing list >>>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>>>> >>>>>>> ------------------------------ >>>>>>> OpenSCAD mailing list >>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>>> >>>>>> ------------------------------ >>>>>> OpenSCAD mailing list >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>> >>>>> ------------------------------ >>>>> OpenSCAD mailing list >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org <discuss-leave@lists.openscad.org>> >>>>> >>>> ------------------------------ >>>> OpenSCAD mailing list >>>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org <discuss-leave@lists.openscad.org>> >>>> >>> ------------------------------ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org >> >> _______________________________________________ > OpenSCAD mailing list > To unsubscribe send an email to discuss-leave@lists.openscad.org
CC
Cory Cross
Sat, Aug 23, 2025 12:48 AM

I've had intermittent Internet access so I've been queuing up messages and didn't see yours first.

I'm not suggesting never adding maps; in fact I've suggested shipping it as implemented (just map) and taking a little more time to figure out how the OO should be done.

On August 22, 2025 8:06:21 PM EDT, Adrian Mariano via Discuss discuss@lists.openscad.org wrote:

Perhaps you're not aware that I'm one of the BOSL2 authors, so when I say
"I implemented" that means it's in BOSL2.

Yes, if I'd seen your message I wouldn't have sent my last one!

Changing struct_val to l just
makes it more confusing to read because the function calls are now very
short.

If it's a convention, to me it's not any different to me than getting used to seeing / instead of ÷ or divide.

My greater point is more that just having maps alone is not going to make everybody's code better. The answer might be just evangelism and documentation, but it also might be taking design lessons from languages with immutable data (like Erlang or Clojure) than copying JS's object system. I'm back to having a computer so I'll type up more code suggestions than prose in the next few days.

  • Cory

It's not about character count exactly---I type very fast---but
about syntactic simplicity and clarity---code readability.  Basically any
time I use these I end up writing foo = struct_val(thing,"foo") to avoid
the syntactic mess that arises from direct access to the structure.  (I am
not a fan of python dicts where you need quotes either.  I return
namedtuples from my functions instead of dicts.)

On Fri, Aug 22, 2025 at 7:58 PM Cory Cross via Discuss <
discuss@lists.openscad.org> wrote:

And, of course it's already done in BOSL2:
https://github.com/BelfrySCAD/BOSL2/wiki/structs.scad

Throw in l=struct_val; to shorten the number of characters to type.

On August 22, 2025 4:17:31 PM EDT, Cory Cross via Discuss <
discuss@lists.openscad.org> wrote:

On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

This discussion is imho a perfect example of bikeshedding https://en.wiktionary.org/wiki/bikeshedding. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs

As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of struct_var.member it's l(struct_var,"member"). That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key.

  • Cory

... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies.

I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours.

I personally would appreciate a time box so we can make sure this does not get delayed another two years again.

Peter

On 21 Aug 2025, at 23:18, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org discuss@lists.openscad.org>> wrote:

On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org discuss@lists.openscad.org>> wrote:

Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy.

Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion.

We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler.

Following this logic, not adding it is simpler.

What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica.

So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer?

Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly.

"this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added.

Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ...

I am new here so I might not understand the mores in this project.

I am new as well (though a user for many years).

However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ...

I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here.

It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road.

I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right?

Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it.

The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023.

I also think it's important to keep momentum up.

I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-)

I can be a bit blunt but you can blame it on my Dutch ancestry :-).

  • Cory Cross

Peter

Actually, plists are better because you can get super methods :-)

I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors.

On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss discuss@lists.openscad.org wrote:

You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and object? Not good for my blood pressure! :-)

I like the builder approach and it is one of my drivers for the object work and $this.

BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ...

Peter Kriens

On 19 Aug 2025, at 07:47, Cory Cross via Discuss discuss@lists.openscad.org wrote:

On 8/16/25 8:49 AM, Jordan Brown wrote:

bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify();

I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all.  Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function?
If the former, I don't understand what it means.  If the latter, are you seriously suggesting this as a replacement for
threaded_nut(required, args, here, optional_generic_arc=its_value);

I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add using bosl2::threading to shorten names, at some point).

As a practical example, here is a invocation of a threaded module in my code:

buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK);

here's how I would do it with the builder pattern and the suggested OO approach:

buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify;

You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs:

buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK));

In this case, buttress_threaded_nut's implementation would change from 50 lines to 11:

module buttress_threaded_nut(kwargs) {
profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
];
generic_threaded_nut(struct(kwargs, profile=profile));
}

Of course, this isn't OO and makes it harder to detect argument name typos and such.

The builder pattern could be implemented as so:

// namespace for buttress_threaded_nut_builder
obj = struct(generic_threaded_nut_builder::new());
function new() =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
struct(obj)->profile(profile);

so not any more or less difficult to write. What do profile and positioning look like?

// namespace of generic_threaded_rod_builder
function profile(o is builder_obj, profile) =
assert(is_list(profile)) // And other tests independent of other values
struct(o,profile=profile);

// namespace of attachable_builder
function positioning(o is attachable_builder_obj, anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(o,l);

Okay, not in love with the repetition in there. But there are some improvements here:

  1. Some validation can now stop cluttering the top of so many functions/modules.
  2. The get_radius function doesn't need its args filled out every time
  3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature.
  4. We can match on the old types and convert to objects as needed

Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as object vs struct keywords.
Maybe attachable should be a mixin instead of in the class hierarchy.

So how would I write this with this as currently proposed?

function buttress_threaded_nut_builder =
let ( profile = [
[  -1/2, -0.77],
[ -7/16, -0.75],
[  5/16,  0],
[  7/16,  0],
[  7/16, -0.75],
[  1/ 2, -0.77],
])
object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */);

// in method list of generic_threaded_rod_builder
function set_profile(profile) =
assert(is_list(profile)) // And other tests independent of other values
object(this,profile=profile);

// in method list of attachable_builder
function set_positioning(anchor, spin, orient) =
assert(/* tests related to the parameters*/)
let(
l = concat(is_undef(anchor) ? [] : [["anchor", anchor]],
is_undef(spin) ? [] : [["spin", spin]],
is_undef(orient) ? [] : [["orient", orient]])
)
struct(this,l);

module reify_buttress_threaded_nut(obj) {
reify_threaded_nut(obj);
}

module reify_threaded_nut(obj) {
reify_generic_threaded_nut(obj);
}

reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK));

...

Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be:

bosl2_threading = object(
top_level_constant = 27,
function buttress_threaded_nut_builder() =
...
);

module reify_buttress_threaded_nut(obj) {
bosl2_threading.top_level_constant; // for whatever reason
reify_threaded_nut(obj);
}

I've seen worse. I certainly can't say this rules out this.

OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org discuss-leave@lists.openscad.org>


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org discuss-leave@lists.openscad.org>


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org


OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org

I've had intermittent Internet access so I've been queuing up messages and didn't see yours first. I'm not suggesting never adding maps; in fact I've suggested shipping it as implemented (just map) and taking a little more time to figure out how the OO should be done. On August 22, 2025 8:06:21 PM EDT, Adrian Mariano via Discuss <discuss@lists.openscad.org> wrote: >Perhaps you're not aware that I'm one of the BOSL2 authors, so when I say >"I implemented" that means it's in BOSL2. Yes, if I'd seen your message I wouldn't have sent my last one! > Changing struct_val to l just >makes it more confusing to read because the function calls are now very >short. If it's a convention, to me it's not any different to me than getting used to seeing `/` instead of `÷` or `divide`. My greater point is more that just having maps alone is not going to make everybody's code better. The answer might be just evangelism and documentation, but it also might be taking design lessons from languages with immutable data (like Erlang or Clojure) than copying JS's object system. I'm back to having a computer so I'll type up more code suggestions than prose in the next few days. - Cory > It's not about character count exactly---I type very fast---but >about syntactic simplicity and clarity---code readability. Basically any >time I use these I end up writing foo = struct_val(thing,"foo") to avoid >the syntactic mess that arises from direct access to the structure. (I am >not a fan of python dicts where you need quotes either. I return >namedtuples from my functions instead of dicts.) > >On Fri, Aug 22, 2025 at 7:58 PM Cory Cross via Discuss < >discuss@lists.openscad.org> wrote: > >> And, of course it's already done in BOSL2: >> https://github.com/BelfrySCAD/BOSL2/wiki/structs.scad >> >> Throw in `l=struct_val;` to shorten the number of characters to type. >> >> >> On August 22, 2025 4:17:31 PM EDT, Cory Cross via Discuss < >> discuss@lists.openscad.org> wrote: >> >>> >>> >>> On August 22, 2025 10:14:28 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >>> >>>> This discussion is imho a perfect example of bikeshedding <https://en.wiktionary.org/wiki/bikeshedding>. We can discuss this forever on a forum like this because we're all highly experienced & opinionated but in the mean time OpenSCAD has no KV pairs >>>> >>> >>> As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of `struct_var.member` it's `l(struct_var,"member")`. That's 17 vs 22 characters or 29% more for a typical case. As a bonus, this userspace method is backwards-compatible and forward-compatible with an optimized implementation which does also allow struct_var.member syntax and allows any type as a key. >>> >>> - Cory >>> >>> >>> >>> ... The enemy of good is 'better' ... This is exactly how the OEP8 effort got derailed more than 2 years ago. At a certain moment everybody gets confused and it dies. >>>> >>>> I suggest you make a working PR so we can play with concrete proposals instead of trying to infer what your sketches mean. If I can use KV pairs and get some concise way to make methods I happily close my PR and support yours. >>>> >>>> I personally would appreciate a time box so we can make sure this does not get delayed another two years again. >>>> >>>> Peter >>>> >>>> >>>> On 21 Aug 2025, at 23:18, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>>>> >>>>> >>>>> >>>>> On August 21, 2025 9:57:30 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org <discuss@lists.openscad.org>>> wrote: >>>>> >>>>>> On 21 Aug 2025, at 01:55, Cory Cross via Discuss <discuss@lists.openscad.org <mailto:discuss@lists.openscad.org <discuss@lists.openscad.org>>> wrote: >>>>>>> >>>>>> Objects do NOT have parents so I am not sure what you're talking about? An object is a flat set of key-value pairs. There is no hierarchy. >>>>>> >>>>> >>>>> Then they shouldn't be called objects. At least 99% of people who have or will use OpenSCAD will associate objects with the mainstream object-oriented languages which all put inheritance front-and-center. It's day-one "learning Python" material. I don't think there's a single language with a "this" keyword that doesn't have inheritance. You're setting up people for confusion. >>>>> >>>>> We are very intentionally not implementing a full blow OO system to keep OpenSCAD as simple as possible, but not simpler. >>>>>> >>>>> >>>>> Following this logic, not adding it is simpler. >>>>> >>>>> What do you want to be simple: writing SCAD or the implementation of OpenSCAD? There's often (but not always) a trade-off. Brainfuck is very simple to implement. You can solve some very complicated analyses in a single line of Mathematica. >>>>> >>>>> So we already have the power to do builders, it's just slightly uglier and slower. My question is while settle for "this" when generic functions are even nicer? >>>>>>> >>>>>> >>>>>> Slightly??? We must live in another universe. There are few types of code I'd like to write less than this kind of boiler plate code. I wrote several of these 'OO' systems but they were quite ugly. >>>>>> >>>>> >>>>> "this" refers to the proposal to add a "this" keyword. But what I wrote shows that, with a couple helper functions, the proposed object() does not result in substantially simpler code and by the maxim "keep OpenSCAD as simple as possible, but not simpler", shouldn't be added. >>>>> >>>>> Then you threw in an a-bom ... and another one. I am getting a bit desperate and feel this is going way off the track and taking way too much of my time ... >>>>>> >>>>>> I am new here so I might not understand the mores in this project. >>>>>> >>>>> >>>>> I am new as well (though a user for many years). >>>>> >>>>> However, in other projects I am used that if you want to derail a PR you make a fully working counter PR so people can play with the proposals and compare. I find that you're now just dropping disruptive ideas ... >>>>>> >>>>> >>>>> I've not found any other discussion of OEP8 and wasn't active at the time anyway. I am discussing now because now is when I'm here. >>>>> >>>>> It's my impression the "this" keyword is just being added because people are unfamiliar with other systems. If SCAD was a hybrid procedural/OO lisp with mutable values like JavaScript, then it'd be fine to copy their semantics. But it's not and I think you're going down the wrong road. >>>>> >>>>> I'm trying to prove it by picking bosl2 and showing how I'd refactor it using the proposed "this" approach or the one I'm proposing, because ultimately what we want is what makes it easier and faster to write correct code, right? >>>>> >>>>> Everything I've proposed is quite easy to implement and I'll be happy to do it and/or collaborate on it. >>>>> >>>>> The danger here is that we spend a lot of time talking back and forth and then nothing happens again because everything got so complicated. I think this partly happened with OEP8 and that spent a lot of time in discussion. There are very good, some crucial, ideas in that PR that has been idling since 2023. >>>>>> >>>>> >>>>> I also think it's important to keep momentum up. >>>>> >>>>> I know I can be a bit blunt but you can blame it on my Dutch citizenship ;-) >>>>>> >>>>> >>>>> I can be a bit blunt but you can blame it on my Dutch ancestry :-). >>>>> >>>>> - Cory Cross >>>>> >>>>> >>>>>> Peter >>>>>> >>>>>> >>>>>> >>>>>> >>>>>>> Actually, plists are better because you can get super methods :-) >>>>>>> >>>>>>> >>>>>>> I'm on my phone composing this without Internet access, so please forgive the formatting and mild syntax errors. >>>>>>> >>>>>>> >>>>>>> >>>>>>> On August 19, 2025 4:40:20 AM EDT, Peter Kriens via Discuss <discuss@lists.openscad.org> wrote: >>>>>>> >>>>>>>> You flabbergasted me with the completely different direction/syntax you took but then at the end I saw that you came to the conclusion you could do all this also with 'this' and `object`? Not good for my blood pressure! :-) >>>>>>>> >>>>>>>> I like the builder approach and it is one of my drivers for the object work and $this. >>>>>>>> >>>>>>>> BTW, notice that OEP8 also proposed to have modules as expression. I am currently working on a PR for this. This will allow builders to also call modules, have modules as variables, and hopefully modules as methods when we can finally close [the $]this discussion ... >>>>>>>> >>>>>>>> Peter Kriens >>>>>>>> >>>>>>>> On 19 Aug 2025, at 07:47, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: >>>>>>>>> On 8/16/25 8:49 AM, Jordan Brown wrote: >>>>>>>>> >>>>>>>>>> bosl2::threading::nut_builder::new(required, args, here)->optional_generic_arg(its_value)->reify(); >>>>>>>>>>> >>>>>>>>>> I'm very sympathetic to the desire to reduce repetition in argument handling, but I'm not understanding what that means at all. Partly that's presentation; is this intended to be how the library would say something, or how the caller would invoke the function? >>>>>>>>>> If the former, I don't understand what it means. If the latter, are you seriously suggesting this as a replacement for >>>>>>>>>> threaded_nut(required, args, here, optional_generic_arc=its_value); >>>>>>>>>> >>>>>>>>> I am suggesting it as a replacement for the latter; not because it's better for the user, but because it's better for the maintainers and not worse for the users. (I would assume we'd add `using bosl2::threading` to shorten names, at some point). >>>>>>>>> >>>>>>>>> As a practical example, here is a invocation of a threaded module in my code: >>>>>>>>> >>>>>>>>> buttress_threaded_nut(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK); >>>>>>>>> >>>>>>>>> here's how I would do it with the builder pattern and the suggested OO approach: >>>>>>>>> >>>>>>>>> buttress_threaded_nut_builder::new()->nutwidth(10)->id(7+.17*4)->h(6)->shape("square")->positioning(orient=DOWN,anchor=TOP+BACK)->reify; >>>>>>>>> >>>>>>>>> You don't have to use the builder pattern. You could choose to use objects as a replacement for Python's **kwargs: >>>>>>>>> >>>>>>>>> buttress_threaded_nut(struct(nutwidth=10,id=7+.17*4,h=6,pitch=1,shape="square",orient=DOWN,anchor=TOP+BACK)); >>>>>>>>> >>>>>>>>> In this case, buttress_threaded_nut's implementation would change from 50 lines to 11: >>>>>>>>> >>>>>>>>> module buttress_threaded_nut(kwargs) { >>>>>>>>> profile = [ >>>>>>>>> [ -1/2, -0.77], >>>>>>>>> [ -7/16, -0.75], >>>>>>>>> [ 5/16, 0], >>>>>>>>> [ 7/16, 0], >>>>>>>>> [ 7/16, -0.75], >>>>>>>>> [ 1/ 2, -0.77], >>>>>>>>> ]; >>>>>>>>> generic_threaded_nut(struct(kwargs, profile=profile)); >>>>>>>>> } >>>>>>>>> >>>>>>>>> Of course, this isn't OO and makes it harder to detect argument name typos and such. >>>>>>>>> >>>>>>>>> The builder pattern could be implemented as so: >>>>>>>>> >>>>>>>>> // namespace for buttress_threaded_nut_builder >>>>>>>>> obj = struct(generic_threaded_nut_builder::new()); >>>>>>>>> function new() = >>>>>>>>> let ( profile = [ >>>>>>>>> [ -1/2, -0.77], >>>>>>>>> [ -7/16, -0.75], >>>>>>>>> [ 5/16, 0], >>>>>>>>> [ 7/16, 0], >>>>>>>>> [ 7/16, -0.75], >>>>>>>>> [ 1/ 2, -0.77], >>>>>>>>> ]) >>>>>>>>> struct(obj)->profile(profile); >>>>>>>>> >>>>>>>>> so not any more or less difficult to write. What do profile and positioning look like? >>>>>>>>> >>>>>>>>> // namespace of generic_threaded_rod_builder >>>>>>>>> function profile(o is builder_obj, profile) = >>>>>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>>>>> struct(o,profile=profile); >>>>>>>>> >>>>>>>>> // namespace of attachable_builder >>>>>>>>> function positioning(o is attachable_builder_obj, anchor, spin, orient) = >>>>>>>>> assert(/* tests related to the parameters*/) >>>>>>>>> let( >>>>>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>>>>> ) >>>>>>>>> struct(o,l); >>>>>>>>> >>>>>>>>> Okay, not in love with the repetition in there. But there are some improvements here: >>>>>>>>> >>>>>>>>> 1. Some validation can now stop cluttering the top of so many functions/modules. >>>>>>>>> 2. The `get_radius` function doesn't need its args filled out every time >>>>>>>>> 3. We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature. >>>>>>>>> 4. We can match on the old types and convert to objects as needed >>>>>>>>> >>>>>>>>> Unsolved issue: why would -> method invocation not look at the namespace of positioning? I didn't intend it to, but it would do the wrong thing as written; only the calls in the new() methods should add the namespace to the method lookup. This might be best as `object` vs `struct` keywords. >>>>>>>>> Maybe attachable should be a mixin instead of in the class hierarchy. >>>>>>>>> >>>>>>>>> So how would I write this with `this` as currently proposed? >>>>>>>>> >>>>>>>>> function buttress_threaded_nut_builder = >>>>>>>>> let ( profile = [ >>>>>>>>> [ -1/2, -0.77], >>>>>>>>> [ -7/16, -0.75], >>>>>>>>> [ 5/16, 0], >>>>>>>>> [ 7/16, 0], >>>>>>>>> [ 7/16, -0.75], >>>>>>>>> [ 1/ 2, -0.77], >>>>>>>>> ]) >>>>>>>>> object(new_generic_threaded_rod_builder().set_profile(profile), /* all methods on buttress_threaded_nut_builder must be defined here */); >>>>>>>>> >>>>>>>>> // in method list of generic_threaded_rod_builder >>>>>>>>> function set_profile(profile) = >>>>>>>>> assert(is_list(profile)) // And other tests independent of other values >>>>>>>>> object(this,profile=profile); >>>>>>>>> >>>>>>>>> // in method list of attachable_builder >>>>>>>>> function set_positioning(anchor, spin, orient) = >>>>>>>>> assert(/* tests related to the parameters*/) >>>>>>>>> let( >>>>>>>>> l = concat(is_undef(anchor) ? [] : [["anchor", anchor]], >>>>>>>>> is_undef(spin) ? [] : [["spin", spin]], >>>>>>>>> is_undef(orient) ? [] : [["orient", orient]]) >>>>>>>>> ) >>>>>>>>> struct(this,l); >>>>>>>>> >>>>>>>>> module reify_buttress_threaded_nut(obj) { >>>>>>>>> reify_threaded_nut(obj); >>>>>>>>> } >>>>>>>>> >>>>>>>>> >>>>>>>>> module reify_threaded_nut(obj) { >>>>>>>>> reify_generic_threaded_nut(obj); >>>>>>>>> } >>>>>>>>> >>>>>>>>> reify_buttress_threaded_nut(buttress_threaded_nut_builder().set_nutwidth(10)->set_id(7+.17*4)->set_h(6)->set_shape("square")->set_positioning(orient=DOWN,anchor=TOP+BACK)); >>>>>>>>> >>>>>>>>> >>>>>>>>> ... >>>>>>>>> >>>>>>>>> Less different than I thought. Cascading explicit reify modules are annoying. For this usage the inability to write your own generic methods dispatching on other object types does not hinder anything. The shared namespace means methods and values must have unique names. I think this is supposed to solve name conflicts by having you only need one unique name per file and you put all your constants in there? And methods, I guess, so at the top of f.ex. bosl2/threading.scad there would be: >>>>>>>>> >>>>>>>>> bosl2_threading = object( >>>>>>>>> top_level_constant = 27, >>>>>>>>> function buttress_threaded_nut_builder() = >>>>>>>>> ... >>>>>>>>> ); >>>>>>>>> >>>>>>>>> module reify_buttress_threaded_nut(obj) { >>>>>>>>> bosl2_threading.top_level_constant; // for whatever reason >>>>>>>>> reify_threaded_nut(obj); >>>>>>>>> } >>>>>>>>> >>>>>>>>> I've seen worse. I certainly can't say this rules out `this`. >>>>>>>>> ------------------------------ >>>>>>>>> OpenSCAD mailing list >>>>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>>>>> >>>>>>>> ------------------------------ >>>>>>>> OpenSCAD mailing list >>>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>>>> >>>>>>> ------------------------------ >>>>>>> OpenSCAD mailing list >>>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>>>>>> >>>>>> ------------------------------ >>>>>> OpenSCAD mailing list >>>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org <discuss-leave@lists.openscad.org>> >>>>>> >>>>> ------------------------------ >>>>> OpenSCAD mailing list >>>>> To unsubscribe send an email to discuss-leave@lists.openscad.org <mailto:discuss-leave@lists.openscad.org <discuss-leave@lists.openscad.org>> >>>>> >>>> ------------------------------ >>> OpenSCAD mailing list >>> To unsubscribe send an email to discuss-leave@lists.openscad.org >>> >>> _______________________________________________ >> OpenSCAD mailing list >> To unsubscribe send an email to discuss-leave@lists.openscad.org
RD
Revar Desmera
Sat, Aug 23, 2025 5:04 AM

On Aug 22, 2025, at 1:17 PM, Cory Cross via Discuss discuss@lists.openscad.org wrote:

As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of struct_var.member it's l(struct_var,"member").

We did implement it in BOSL2.  Years ago. It's called struct(). It is very inefficient and a poor substitute for a real associative array.

  • Revar
> On Aug 22, 2025, at 1:17 PM, Cory Cross via Discuss <discuss@lists.openscad.org> wrote: > > As I pointed out before, this isn't true. If you want kv pairs it's trivially implemented with alists. Instead of `struct_var.member` it's `l(struct_var,"member")`. We did implement it in BOSL2. Years ago. It's called `struct()`. It is very inefficient and a poor substitute for a real associative array. - Revar
RD
Revar Desmera
Sat, Aug 23, 2025 5:45 AM

I didn't get much choice in naming the object() function when I wrote it. The is_object() function had already existed for a year or so at the time. Also, the textmetrics() and fontmetrics() calls already had their return type called objects. Otherwise I would have called the type a dictionary, and the function dict().

Really, object() isn't something that a regular user is going to use often, much less the $this variable, or module literals. Simple projects do not need object orientation.  This is a set of features targeted for library writers, or large code bases, or for complicated algorithms.  The vast majority of users will only need to know the dot (.) syntax to use it, preserving the apparent simplicity of the language.

bosl = use("BOSL3/std.scad");
bosl.Cuboid([30,40,50])
.Chamfer(5, angle=60)
.Edges([bosl.TOP, bosl.LEFT],except=bosl.TOP+bosl.LEFT)
.Show();

Note that builder design pattern makes the code much more verbose, so that seems like an awful example.

  • Revar
I didn't get much choice in naming the `object()` function when I wrote it. The `is_object()` function had already existed for a year or so at the time. Also, the `textmetrics()` and `fontmetrics()` calls already had their return type called objects. Otherwise I would have called the type a dictionary, and the function `dict()`. Really, `object()` isn't something that a regular user is going to use often, much less the `$this` variable, or module literals. Simple projects do not need object orientation. This is a set of features targeted for library writers, or large code bases, or for complicated algorithms. The vast majority of users will only need to know the dot (.) syntax to use it, preserving the apparent simplicity of the language. bosl = use("BOSL3/std.scad"); bosl.Cuboid([30,40,50]) .Chamfer(5, angle=60) .Edges([bosl.TOP, bosl.LEFT],except=bosl.TOP+bosl.LEFT) .Show(); Note that builder design pattern makes the code much more verbose, so that seems like an awful example. - Revar