L
larry
Sat, Aug 23, 2025 6:03 AM
On Fri, 2025-08-22 at 22:45 -0700, Revar Desmera via Discuss wrote:
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();
Hmm... should that work in 2025.08.19.ai26698 ?
On Fri, 2025-08-22 at 22:45 -0700, Revar Desmera via Discuss wrote:
> > > > 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();
Hmm... should that work in 2025.08.19.ai26698 ?
PK
Peter Kriens
Sat, Aug 23, 2025 12:59 PM
Note that builder design pattern makes the code much more verbose, so that seems like an awful example.
I agree that the builder pattern has much less oomph in Openscad than in Java and other such languages. The named parameters with defaults in a function/module call handle many of the cases where one needs a builder in Java . I noticed in this work that many intuitions based on 30 year Java tend to not work well in OpenSCAD. That said, builders have their advantages in some cases.
My favorite use case for object() is a prism object (a solid between two paths). I am addicted to rounded_prism in BOSL2. However many of my shapes are hollow and closed/open on the bottom/top and then you need to calculate different variations of the same geometry. Hiding the detailed operations behind an object facade really cleans up the code and makes it a lot more readable. When I look at BOSL2 it will make a tremendous difference for the readability if something like the geom and vnf structures were behind an object facade.
I already had a home made object system a la Cory using strings for the method names and fields, using object() now makes it much more readable as Adrian also indicated because you can use the well known dot notation. The lack of $this however still requires a lot of ugly repetitive boiler plate cruft. (Which makes me completely flummoxed to the opposition to this !)
Peter
Sent from my iPad
On 23 Aug 2025, at 07:46, Revar Desmera via Discuss discuss@lists.openscad.org wrote:
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.
OpenSCAD mailing list
To unsubscribe send an email to discuss-leave@lists.openscad.org
> Note that builder design pattern makes the code much more verbose, so that seems like an awful example.
I agree that the builder pattern has much less oomph in Openscad than in Java and other such languages. The named parameters with defaults in a function/module call handle many of the cases where one needs a builder in Java . I noticed in this work that many intuitions based on 30 year Java tend to not work well in OpenSCAD. That said, builders have their advantages in some cases.
My favorite use case for object() is a prism object (a solid between two paths). I am addicted to rounded_prism in BOSL2. However many of my shapes are hollow and closed/open on the bottom/top and then you need to calculate different variations of the same geometry. Hiding the detailed operations behind an object facade really cleans up the code and makes it a lot more readable. When I look at BOSL2 it will make a tremendous difference for the readability if something like the geom and vnf structures were behind an object facade.
I already had a home made object system a la Cory using strings for the method names and fields, using object() now makes it much more readable as Adrian also indicated because you can use the well known dot notation. The lack of $this however still requires a lot of ugly repetitive boiler plate cruft. (Which makes me completely flummoxed to the opposition to this !)
Peter
Sent from my iPad
> On 23 Aug 2025, at 07:46, Revar Desmera via Discuss <discuss@lists.openscad.org> wrote:
>
> 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
>
> _______________________________________________
> OpenSCAD mailing list
> To unsubscribe send an email to discuss-leave@lists.openscad.org
PK
Peter Kriens
Sat, Aug 23, 2025 1:47 PM
Then make a proposal.
I don’t give a rats ass how things are called because a keyword in a language is always mnemonic, never definitional. (I managed spec writing for 20 years of my life.) If you think the name is so all important propose ONE name and then I can sit back and watch another few weeks of bikeshedding here to get consensus. I’ll even rename the built in function for you once that happens!
However, for me it is absolutely crucial to have a mechanism for methods: functions that can access their owning map, key-value pairs, associative array, dictionary, symbol table, struct, record, lookup table, registry, index, function, and dare I say it, object,foobars, etc. because it helps scoping the names.
Today, libraries need to declare global names for functions and variables today, methods allow them to keep the scope local. Today you therefore need to come up with unwieldy names to make them globally unique. For example, BOSL had to invent new names for cube (cuboid) because the nice names were taken. With foobars they can have their own scope. I strongly believe it also makes complex code much more readable.
Since you have derailed that as well, any proposal for this?
Sent from my iPad
Let's stop trying to make them objects and just call them dictionary, map, associative array, or any of the many other names for this: https://en.m.wikipedia.org/wiki/Associative_array
The words you use carry connotations and have denotations that users will use to understand what it is. If you offer to pay 100 Euros and show up with a 1-Euro bill, the other party is going to be upset and confused even if you explain you don't pronounce the "ents" of "cents".
I personally would appreciate a time box so we can make sure this does not get delayed another two years again.
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 :-).
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
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:
- Some validation can now stop cluttering the top of so many functions/modules.
- The
get_radius function doesn't need its args filled out every time
- We're reusing attachable instead of needing to redundantly pass it so many args every single time and in every function and module signature.
- 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
Then make a proposal.
I don’t give a rats ass how things are called because a keyword in a language is always mnemonic, never definitional. (I managed spec writing for 20 years of my life.) If you think the name is so all important propose ONE name and then I can sit back and watch another few weeks of bikeshedding here to get consensus. I’ll even rename the built in function for you once that happens!
However, for me it is absolutely crucial to have a mechanism for _methods_: functions that can access their owning map, key-value pairs, associative array, dictionary, symbol table, struct, record, lookup table, registry, index, function, and dare I say it, object,foobars, etc. because it helps scoping the names.
Today, libraries need to declare global names for functions and variables today, methods allow them to keep the scope local. Today you therefore need to come up with unwieldy names to make them globally unique. For example, BOSL had to invent new names for cube (cuboid) because the nice names were taken. With foobars they can have their own scope. I strongly believe it also makes complex code much more readable.
Since you have derailed that as well, any proposal for this?
Sent from my iPad
> On 22 Aug 2025, at 18:06, 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
>
> Let's stop trying to make them objects and just call them dictionary, map, associative array, or any of the many other names for this: https://en.m.wikipedia.org/wiki/Associative_array
>
> The words you use carry connotations and have denotations that users will use to understand what it is. If you offer to pay 100 Euros and show up with a 1-Euro bill, the other party is going to be upset and confused even if you explain you don't pronounce the "ents" of "cents".
>
> - Cory
>
>
>> 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
JB
Jon Bondy
Sat, Aug 23, 2025 2:02 PM
Excellent. Requirements!
It is absolutely crucial to have a mechanism for methods: functions
that can access their owning map, key-value pairs, associative array,
dictionary, symbol table, struct, record, lookup table, registry, index,
function, and dare I say it, object,foobars, etc. /because it helps
scoping the names/.
Today, libraries need to declare global names for functions and
variables today, methods allow them to keep the scope local. Today you
therefore need to come up with unwieldy names to make them globally
unique. For example, BOSL had to invent new names for cube (cuboid)
because the nice names were taken. With foobars they can have their own
scope. I strongly believe it also makes complex code much more readable.
On 8/23/2025 9:47 AM, Peter Kriens via Discuss wrote:
Then make a proposal.
I don’t give a rats ass how things are called because a keyword in a
language is always /mnemonic/, never /definitional/. (I managed spec
writing for 20 years of my life.) If you think the name is so all
important propose ONE name and then I can sit back and watch another
few weeks of bikeshedding here to get consensus. I’ll even rename the
built in function for you once that happens!
However, for me it is absolutely crucial to have a mechanism for
methods: functions that can access their owning map, key-value
pairs, associative array, dictionary, symbol table, struct, record,
lookup table, registry, index, function, and dare I say it,
object,foobars, etc. /because it helps scoping the names/.
Today, libraries need to declare global names for functions and
variables today, methods allow them to keep the scope local. Today you
therefore need to come up with unwieldy names to make them globally
unique. For example, BOSL had to invent new names for cube (cuboid)
because the nice names were taken. With foobars they can have their
own scope. I strongly believe it also makes complex code much more
readable.
Since you have derailed that as well, any proposal for this?
Sent from my iPad
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
Let's stop trying to make them objects and just call them dictionary,
map, associative array, or any of the many other names for this:
https://en.m.wikipedia.org/wiki/Associative_array
The words you use carry connotations and have denotations that users
will use to understand what it is. If you offer to pay 100 Euros and
show up with a 1-Euro bill, the other party is going to be upset and
confused even if you explain you don't pronounce the "ents" of "cents".
I personally would appreciate a time box so we can make sure this
does not get delayed another two years again.
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 :-).
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
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:
- Some validation can now stop cluttering the top of so many
functions/modules.
- The
get_radius function doesn't need its args filled out
every time
- We're reusing attachable instead of needing to redundantly
pass it so many args every single time and in every function
and module signature.
- 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
--
This email has been checked for viruses by AVG antivirus software.
www.avg.com
Excellent. Requirements!
It is absolutely crucial to have a mechanism for _methods_: functions
that can access their owning map, key-value pairs, associative array,
dictionary, symbol table, struct, record, lookup table, registry, index,
function, and dare I say it, object,foobars, etc. /because it helps
scoping the names/.
Today, libraries need to declare global names for functions and
variables today, methods allow them to keep the scope local. Today you
therefore need to come up with unwieldy names to make them globally
unique. For example, BOSL had to invent new names for cube (cuboid)
because the nice names were taken. With foobars they can have their own
scope. I strongly believe it also makes complex code much more readable.
On 8/23/2025 9:47 AM, Peter Kriens via Discuss wrote:
> Then make a proposal.
>
> I don’t give a rats ass how things are called because a keyword in a
> language is always /mnemonic/, never /definitional/. (I managed spec
> writing for 20 years of my life.) If you think the name is so all
> important propose ONE name and then I can sit back and watch another
> few weeks of bikeshedding here to get consensus. I’ll even rename the
> built in function for you once that happens!
>
> However, for me it is absolutely crucial to have a mechanism for
> _methods_: functions that can access their owning map, key-value
> pairs, associative array, dictionary, symbol table, struct, record,
> lookup table, registry, index, function, and dare I say it,
> object,foobars, etc. /because it helps scoping the names/.
>
> Today, libraries need to declare global names for functions and
> variables today, methods allow them to keep the scope local. Today you
> therefore need to come up with unwieldy names to make them globally
> unique. For example, BOSL had to invent new names for cube (cuboid)
> because the nice names were taken. With foobars they can have their
> own scope. I strongly believe it also makes complex code much more
> readable.
>
> Since you have derailed that as well, any proposal for this?
>
>
> Sent from my iPad
>
>> On 22 Aug 2025, at 18:06, 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
>>
>> Let's stop trying to make them objects and just call them dictionary,
>> map, associative array, or any of the many other names for this:
>> https://en.m.wikipedia.org/wiki/Associative_array
>>
>> The words you use carry connotations and have denotations that users
>> will use to understand what it is. If you offer to pay 100 Euros and
>> show up with a 1-Euro bill, the other party is going to be upset and
>> confused even if you explain you don't pronounce the "ents" of "cents".
>>
>> - Cory
>>
>>
>>> 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
>
> _______________________________________________
> OpenSCAD mailing list
> To unsubscribe send an email todiscuss-leave@lists.openscad.org
--
This email has been checked for viruses by AVG antivirus software.
www.avg.com
CC
Cory Cross
Sat, Aug 23, 2025 3:23 PM
On 8/23/25 6:47 AM, Peter Kriens via Discuss wrote:
We're discussing my proposal now.
However, for me it is absolutely crucial to have a mechanism for
methods: functions that can access their owning map, key-value
pairs, associative array, dictionary, symbol table, struct, record,
lookup table, registry, index, function, and dare I say it,
object,foobars, etc. /because it helps scoping the names/.
The language Elixir (and Erlang), a language with immutable types, has
namespacing without methods
https://hexdocs.pm/elixir/modules-and-functions.html:
|defmoduleMathdodefsum(a,b)doa+bendendIO.putsMath.sum(1,2)|
To summarize the above, this defines a namespace called Math. The
function sum is defined in the namespace Math. The last line invokes the
function puts from the namespace IO with a single argument, which is the
result from calling the function sum in the namespace Math with two
numerical arguments, 1 and 2.
So you can't write "something".capitalize() but you write
String.Capitalize "something". This solves your requirement of /because
it helps scoping the names/.
We could have:
namespace bosl2_threading {
module generic_nut(<existing args>) { <existing body> }
module nut(<existing args>) { <existing body>
generic_nut(<existing>) } // Do not have to specify namespace for
functions/modules in same namespace.
}
And instead of existing code of
threaded_nut(<existing args>);
we have
bosl2_threading.nut(<existing args>);
and/or
using bosl2_threading;
nut(<existing args>);
nut(<existing args>);
nut(<existing args>);
Since you have derailed that as well, any proposal for this?
I'm amused at my power, but please let me know what you think about the
above. This means leaving "object" as it's currently implemented, a map.
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
Let's stop trying to make them objects and just call them dictionary,
map, associative array, or any of the many other names for this:
https://en.m.wikipedia.org/wiki/Associative_array
The words you use carry connotations and have denotations that users
will use to understand what it is. If you offer to pay 100 Euros and
show up with a 1-Euro bill, the other party is going to be upset and
confused even if you explain you don't pronounce the "ents" of "cents".
I personally would appreciate a time box so we can make sure this
does not get delayed another two years again.
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 :-).
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
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:
- Some validation can now stop cluttering the top of so many
functions/modules.
- The
get_radius function doesn't need its args filled out
every time
- We're reusing attachable instead of needing to redundantly
pass it so many args every single time and in every function
and module signature.
- 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
On 8/23/25 6:47 AM, Peter Kriens via Discuss wrote:
> Then make a proposal.
We're discussing my proposal now.
> However, for me it is absolutely crucial to have a mechanism for
> _methods_: functions that can access their owning map, key-value
> pairs, associative array, dictionary, symbol table, struct, record,
> lookup table, registry, index, function, and dare I say it,
> object,foobars, etc. /because it helps scoping the names/.
The language Elixir (and Erlang), a language with immutable types, has
namespacing without methods
<https://hexdocs.pm/elixir/modules-and-functions.html>:
|defmoduleMathdodefsum(a,b)doa+bendendIO.putsMath.sum(1,2)|
To summarize the above, this defines a namespace called Math. The
function sum is defined in the namespace Math. The last line invokes the
function puts from the namespace IO with a single argument, which is the
result from calling the function sum in the namespace Math with two
numerical arguments, 1 and 2.
So you can't write "something".capitalize() but you write
String.Capitalize "something". This solves your requirement of /because
it helps scoping the names/.
We could have:
namespace bosl2_threading {
module generic_nut(<existing args>) { <existing body> }
module nut(<existing args>) { <existing body>
generic_nut(<existing>) } // Do not have to specify namespace for
functions/modules in same namespace.
}
And instead of existing code of
threaded_nut(<existing args>);
we have
bosl2_threading.nut(<existing args>);
and/or
using bosl2_threading;
nut(<existing args>);
nut(<existing args>);
nut(<existing args>);
> Since you have derailed that as well, any proposal for this?
I'm amused at my power, but please let me know what you think about the
above. This means leaving "object" as it's currently implemented, a map.
- Cory
>
>
> Sent from my iPad
>
>> On 22 Aug 2025, at 18:06, 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
>>
>> Let's stop trying to make them objects and just call them dictionary,
>> map, associative array, or any of the many other names for this:
>> https://en.m.wikipedia.org/wiki/Associative_array
>>
>> The words you use carry connotations and have denotations that users
>> will use to understand what it is. If you offer to pay 100 Euros and
>> show up with a 1-Euro bill, the other party is going to be upset and
>> confused even if you explain you don't pronounce the "ents" of "cents".
>>
>> - Cory
>>
>>
>>> 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
>
> _______________________________________________
> OpenSCAD mailing list
> To unsubscribe send an email todiscuss-leave@lists.openscad.org
CC
Cory Cross
Sat, Aug 23, 2025 6:15 PM
On 8/22/25 10:45 PM, Revar Desmera via Discuss wrote:
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.
How do you feel about needing to explicitly put anchor, spin, orient in
nearly all function and method signatures? Additionally the threaded
parameters, all the top/bottom inner/outer radius/diameter parameters, etc.
I've still not finished up a new joiner because it seems annoying to add
those things (it works for me, but I want to add these things before
submitting).
I think it'd be really nice if you could mark a module or function as an
attachable and get the final stuff handled for you as much as possible.
I also found it cumbersome and/or easily-missed to add the radius and
diameter options, as did the person writing the PR about the D-ring. I
would find it beneficial to be able to mark my module as having top and
bottom inner and outer circles.
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.
OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org
On 8/22/25 10:45 PM, Revar Desmera via Discuss wrote:
> 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.
How do you feel about needing to explicitly put anchor, spin, orient in
nearly all function and method signatures? Additionally the threaded
parameters, all the top/bottom inner/outer radius/diameter parameters, etc.
I've still not finished up a new joiner because it seems annoying to add
those things (it works for me, but I want to add these things before
submitting).
I think it'd be really nice if you could mark a module or function as an
attachable and get the final stuff handled for you as much as possible.
I also found it cumbersome and/or easily-missed to add the radius and
diameter options, as did the person writing the PR about the D-ring. I
would find it beneficial to be able to mark my module as having top and
bottom inner and outer circles.
- Cory Cross
>
> 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
>
>
> _______________________________________________
> OpenSCAD mailing list
> To unsubscribe send an email todiscuss-leave@lists.openscad.org
JB
Jordan Brown
Sat, Aug 23, 2025 8:37 PM
[ I thought that when we got off the boat into reliable cellular service
I would be more able to keep up. Ha! I have reliable network service
now, but much less time. ]
First, for the people who aren't interested in OO-like mechanisms and
are concerned about increasing complexity... the intent and expectation
is that this evolution will at worst be neutral for beginners, and at
best will improve things. I don't see any reason to expect that today's
OpenSCAD programs will stop working, except perhaps in narrow corner
cases. (For instance, maybe there will be a few new keywords that
can't be used as variable names.) The stuff that we are discussing
about "this" and inheritance will have approximately zero impact on
beginning and intermediate OpenSCAD users. Advanced OpenSCAD users
might want to take advantage of these mechanisms, but unless they want
to interact with libraries that use those mechanisms, they don't have to.
Cory says that inheritance is a "day one" item for learning Python. I
don't happen to agree, but I think for OpenSCAD it is critical that
basically nothing associated with objects is a day-one item, or even a
month-one item. Many users would never use any "object" mechanism, and
most of the rest would use only member access and method calls. Only
very advanced users, primarily library authors, would create objects
with methods, and that's where all this discussion is.
Then they shouldn't be called objects.
As they stand today, they are indeed better mapped to Python
dictionaries. We could call them dictionaries, records, or any of a
dozen other data-only names. But then when and if we add more OO-like
features, starting with Peter's work on a "this" mechanism, we would be
stuck forever with a name that is inappropriate... or, worse, with two
similar-but-different concepts with different names, like Python has.
My hope is to expand into supporting something more OO-ish, while trying
to avoid the complexity of most OO languages, expanding in bite-sized
chunks.
That last, expanding in bite-sized chunks, is important. If we try to
do everything all at once, if we wait to introduce these features until
they do everything that we want, we are unlikely to ever get anything.
There will always be something additional that somebody wants. We need
a sequence of steps, each building on the last, and each allowing for
the next.
Where will we end up? I don't know. I doubt that it will be as
powerful as a "real" OO language... and that doesn't bother me in the
slightest. Perhaps 90% of my interest is satisfied by the data-only
mechanism, especially once we add module references to parallel function
references. (That's not directly related to OO, but plays well with
it.) Probably 90% of the remainder is satisfied with "this". If we
never get beyond that, if we never address that last 1%, I won't lose
any sleep. If you really need more than what we end up with, you can
move over to PythonSCAD.
As for familiarity and similarity to other languages... What I've called
objects in OpenSCAD are patterned after JavaScript objects. Their
access mechanisms are nearly identical to JS objects.[1] With the OEP8
syntax (PR #6081), the creation syntax is a superset of JS's syntax.[2][3]
[1] JS's "for (k in obj)" becomes "for (k = obj)".
[2] OEP8 adds a mechanism for specifying dynamically-constructed
keys, and object comprehensions (a la OpenSCAD list comprehensions).
[3] I believe that with the OEP8 syntax OpenSCAD will be able to
directly consume JSON.
Touching on detail points:
Inheritance can be managed through cloning objects. That yields a
mechanism very similar to JS's "prototype" mechanism. object() already
allows cloning and object and modifying the result.
Like JS's mechanism, calling shadowed parent methods is ...
interesting. (I should note that for work-legacy reasons almost all of
my JS experience is with 2006-era JS.) I have two thoughts on the
subject. First, when you clone the original object, you can copy a
method reference into your own name, preserving access to it:
o1 = object(method=function (arg) ... );
o2 = object(o1, superMethod=o1.method, method=function (arg) this.superMethod(arg) + 1);
That will work without any additional built-in mechanisms (beyond
"this", of course).
Alternatively, if there's a mechanism that allows calling a function and
handing it a "this" (JavaScript's Function.call), you can use that,
combined with access to the "parent" object, to call a parent method.
Handwaving over the details of that mechanism, and using JS's mechanism
for the moment, you would do something like:
o2 = object(o1, method=function (arg) o1.method.call(this, arg) + 1);
Whether a library uses the builder pattern or some other style is up to
the library. One of the other enhancements I'd like to see added is
varargs mechanisms akin to Python's *args and **kwargs mechanisms.
Those mechanisms could be used to avoid duplicated argument parsing in a
library that wants to stick with the named-argument pattern.
I thought there was more that I wanted to write about, but I've lost track.
[ I thought that when we got off the boat into reliable cellular service
I would be more able to keep up. Ha! I have reliable network service
now, but much less time. ]
First, for the people who *aren't* interested in OO-like mechanisms and
are concerned about increasing complexity... the intent and expectation
is that this evolution will at worst be neutral for beginners, and at
best will improve things. I don't see any reason to expect that today's
OpenSCAD programs will stop working, except perhaps in narrow corner
cases. (For instance, *maybe* there will be a few new keywords that
can't be used as variable names.) The stuff that we are discussing
about "this" and inheritance will have approximately zero impact on
beginning and intermediate OpenSCAD users. Advanced OpenSCAD users
might want to take advantage of these mechanisms, but unless they want
to interact with libraries that use those mechanisms, they don't have to.
Cory says that inheritance is a "day one" item for learning Python. I
don't happen to agree, but I think for OpenSCAD it is *critical* that
basically nothing associated with objects is a day-one item, or even a
month-one item. Many users would never use any "object" mechanism, and
most of the rest would use only member access and method calls. Only
very advanced users, primarily library authors, would create objects
with methods, and that's where all this discussion is.
> Then they shouldn't be called objects.
As they stand today, they are indeed better mapped to Python
dictionaries. We could call them dictionaries, records, or any of a
dozen other data-only names. But then when and if we add more OO-like
features, starting with Peter's work on a "this" mechanism, we would be
stuck forever with a name that is inappropriate... or, worse, with two
similar-but-different concepts with different names, like Python has.
My hope is to expand into supporting something more OO-ish, while trying
to avoid the complexity of most OO languages, expanding in bite-sized
chunks.
That last, expanding in bite-sized chunks, is important. If we try to
do everything all at once, if we wait to introduce these features until
they do everything that we want, we are unlikely to ever get anything.
There will always be something additional that somebody wants. We need
a sequence of steps, each building on the last, and each allowing for
the next.
Where will we end up? I don't know. I doubt that it will be as
powerful as a "real" OO language... and that doesn't bother me in the
slightest. Perhaps 90% of my interest is satisfied by the data-only
mechanism, especially once we add module references to parallel function
references. (That's not directly related to OO, but plays well with
it.) Probably 90% of the remainder is satisfied with "this". If we
never get beyond that, if we never address that last 1%, I won't lose
any sleep. If you really need more than what we end up with, you can
move over to PythonSCAD.
As for familiarity and similarity to other languages... What I've called
objects in OpenSCAD are patterned after JavaScript objects. Their
access mechanisms are nearly identical to JS objects.[1] With the OEP8
syntax (PR #6081), the creation syntax is a superset of JS's syntax.[2][3]
[1] JS's "for (k in obj)" becomes "for (k = obj)".
[2] OEP8 adds a mechanism for specifying dynamically-constructed
keys, and object comprehensions (a la OpenSCAD list comprehensions).
[3] I believe that with the OEP8 syntax OpenSCAD will be able to
directly consume JSON.
---
Touching on detail points:
Inheritance can be managed through cloning objects. That yields a
mechanism very similar to JS's "prototype" mechanism. object() already
allows cloning and object and modifying the result.
Like JS's mechanism, calling shadowed parent methods is ...
interesting. (I should note that for work-legacy reasons almost all of
my JS experience is with 2006-era JS.) I have two thoughts on the
subject. First, when you clone the original object, you can copy a
method reference into your own name, preserving access to it:
o1 = object(method=function (arg) ... );
o2 = object(o1, superMethod=o1.method, method=function (arg) this.superMethod(arg) + 1);
That will work without any additional built-in mechanisms (beyond
"this", of course).
Alternatively, if there's a mechanism that allows calling a function and
handing it a "this" (JavaScript's Function.call), you can use that,
combined with access to the "parent" object, to call a parent method.
Handwaving over the details of that mechanism, and using JS's mechanism
for the moment, you would do something like:
o2 = object(o1, method=function (arg) o1.method.call(this, arg) + 1);
Whether a library uses the builder pattern or some other style is up to
the library. One of the other enhancements I'd like to see added is
varargs mechanisms akin to Python's *args and **kwargs mechanisms.
Those mechanisms could be used to avoid duplicated argument parsing in a
library that wants to stick with the named-argument pattern.
I thought there was more that I wanted to write about, but I've lost track.
CC
Cory Cross
Sat, Aug 23, 2025 9:42 PM
On 8/23/25 1:37 PM, Jordan Brown via Discuss wrote:
Cory says that inheritance is a "day one" item for learning Python. I
don't happen to agree, but I think for OpenSCAD it is critical that
basically nothing associated with objects is a day-one item, or even a
month-one item.
My point was that anyone who knows anything about objects and
programming is going to expect inheritance if you say methods and
object, not that someone who learns OpenSCAD needs to know inheritance
to model.
You call it a dictionary and say it has scoped functions, they're going
to think "huh".
As they stand today, they are indeed better mapped to Python
dictionaries. We could call them dictionaries, records, or any of a
dozen other data-only names. But then when and if we add more OO-like
features, starting with Peter's work on a "this" mechanism, we would
be stuck forever with a name that is inappropriate...
My suggestion is that we should not add any OO-like features to the
key-value store unless we've thought through the design.
or, worse, with two similar-but-different concepts with different
names, like Python has. My hope is to expand into supporting
something more OO-ish, while trying to avoid the complexity of most OO
languages, expanding in bite-sized chunks.
Doing things incrementally is exactly how you get "the complexity of
most OO languages". It's exactly why JavaScript has many different ways
to create objects, why C++ has new and delete and std::unique_ptr and
std::shared_ptr and multiple inheritance and virtual inheritance and...
As for familiarity and similarity to other languages... What I've
called objects in OpenSCAD are patterned after JavaScript objects.
Their access mechanisms are nearly identical to JS objects.[1] With
the OEP8 syntax (PR #6081), the creation syntax is a superset of JS's
syntax.[2][3]
You can't straightforwardly translate almost any actual JavaScript code
to OpenSCAD because it mutates objects. I think that makes it a poor
example to copy. My (and many others') complaints about Go is they
ignored almost all of 50 years of language design and are now catching
up in a worse and uglier way (i.e. generics). Having compiler-enforced
Result and Option instead of multiple return values would have prevented
many real bugs I found in my coworkers' code (and, okay, occasionally
mine :-D).
Like JS's mechanism, calling shadowed parent methods is ...
interesting. (I should note that for work-legacy reasons almost all
of my JS experience is with 2006-era JS.) I have two thoughts on the
subject. First, when you clone the original object, you can copy a
method reference into your own name, preserving access to it:
o1 = object(method=function (arg) ... );
o2 = object(o1, superMethod=o1.method, method=function (arg) this.superMethod(arg) + 1);
This is definitely not simple.
That will work without any additional built-in mechanisms (beyond
"this", of course).
Alternatively, if there's a mechanism that allows calling a function
and handing it a "this" (JavaScript's Function.call), you can use
that, combined with access to the "parent" object, to call a parent
method. Handwaving over the details of that mechanism, and using JS's
mechanism for the moment, you would do something like:
o2 = object(o1, method=function (arg) o1.method.call(this, arg) + 1);
Whether a library uses the builder pattern or some other style is up
to the library. One of the other enhancements I'd like to see added
is varargs mechanisms akin to Python's *args and **kwargs mechanisms.
Those mechanisms could be used to avoid duplicated argument parsing in
a library that wants to stick with the named-argument pattern.
I thought there was more that I wanted to write about, but I've lost
track.
OpenSCAD mailing list
To unsubscribe send an email todiscuss-leave@lists.openscad.org
On 8/23/25 1:37 PM, Jordan Brown via Discuss wrote:
> Cory says that inheritance is a "day one" item for learning Python. I
> don't happen to agree, but I think for OpenSCAD it is *critical* that
> basically nothing associated with objects is a day-one item, or even a
> month-one item.
My point was that anyone who knows anything about objects and
programming is going to expect inheritance if you say methods and
object, not that someone who learns OpenSCAD needs to know inheritance
to model.
You call it a dictionary and say it has scoped functions, they're going
to think "huh".
> As they stand today, they are indeed better mapped to Python
> dictionaries. We could call them dictionaries, records, or any of a
> dozen other data-only names. But then when and if we add more OO-like
> features, starting with Peter's work on a "this" mechanism, we would
> be stuck forever with a name that is inappropriate...
>
My suggestion is that we should not add any OO-like features to the
key-value store unless we've thought through the design.
> or, worse, with two similar-but-different concepts with different
> names, like Python has. My hope is to expand into supporting
> something more OO-ish, while trying to avoid the complexity of most OO
> languages, expanding in bite-sized chunks.
>
Doing things incrementally is exactly how you get "the complexity of
most OO languages". It's exactly why JavaScript has many different ways
to create objects, why C++ has new and delete and std::unique_ptr and
std::shared_ptr and multiple inheritance and virtual inheritance and...
> As for familiarity and similarity to other languages... What I've
> called objects in OpenSCAD are patterned after JavaScript objects.
> Their access mechanisms are nearly identical to JS objects.[1] With
> the OEP8 syntax (PR #6081), the creation syntax is a superset of JS's
> syntax.[2][3]
>
You can't straightforwardly translate almost any actual JavaScript code
to OpenSCAD because it mutates objects. I think that makes it a poor
example to copy. My (and many others') complaints about Go is they
ignored almost all of 50 years of language design and are now catching
up in a worse and uglier way (i.e. generics). Having compiler-enforced
Result and Option instead of multiple return values would have prevented
many real bugs I found in my coworkers' code (and, okay, occasionally
mine :-D).
> Like JS's mechanism, calling shadowed parent methods is ...
> interesting. (I should note that for work-legacy reasons almost all
> of my JS experience is with 2006-era JS.) I have two thoughts on the
> subject. First, when you clone the original object, you can copy a
> method reference into your own name, preserving access to it:
>
> o1 = object(method=function (arg) ... );
> o2 = object(o1, superMethod=o1.method, method=function (arg) this.superMethod(arg) + 1);
>
This is definitely not simple.
- Cory Cross
> That will work without any additional built-in mechanisms (beyond
> "this", of course).
>
> Alternatively, if there's a mechanism that allows calling a function
> and handing it a "this" (JavaScript's Function.call), you can use
> that, combined with access to the "parent" object, to call a parent
> method. Handwaving over the details of that mechanism, and using JS's
> mechanism for the moment, you would do something like:
>
> o2 = object(o1, method=function (arg) o1.method.call(this, arg) + 1);
>
>
> Whether a library uses the builder pattern or some other style is up
> to the library. One of the other enhancements I'd like to see added
> is varargs mechanisms akin to Python's *args and **kwargs mechanisms.
> Those mechanisms could be used to avoid duplicated argument parsing in
> a library that wants to stick with the named-argument pattern.
>
>
> I thought there was more that I wanted to write about, but I've lost
> track.
>
>
>
> _______________________________________________
> OpenSCAD mailing list
> To unsubscribe send an email todiscuss-leave@lists.openscad.org
JB
Jordan Brown
Sun, Aug 24, 2025 11:17 PM
As they stand today, they are indeed better mapped to Python
dictionaries. We could call them dictionaries, records, or any of a
dozen other data-only names. But then when and if we add more
OO-like features, starting with Peter's work on a "this" mechanism,
we would be stuck forever with a name that is inappropriate...
My suggestion is that we should not add any OO-like features to the
key-value store unless we've thought through the design.
Should we back out the existing features, because they are "incomplete"?
Should we rename the data type, and preserve that different name forever?
Doing things incrementally is exactly how you get "the complexity of
most OO languages". It's exactly why JavaScript has many different
ways to create objects, why C++ has new and delete and std::unique_ptr
and std::shared_ptr and multiple inheritance and virtual inheritance
and...
And if we try to do everything, that's what we'll get. I think that
this is close to the limit of what we want to implement; I'm not sure
that a super mechanism makes the cut, especially if it is clearly
possible to DIY.
As for familiarity and similarity to other languages... What I've
called objects in OpenSCAD are patterned after JavaScript objects.
Their access mechanisms are nearly identical to JS objects.[1] With
the OEP8 syntax (PR #6081), the creation syntax is a superset of JS's
syntax.[2][3]
You can't straightforwardly translate almost any actual JavaScript
code to OpenSCAD because it mutates objects. I think that makes it a
poor example to copy.
Depending on what you mean by "mutates objects", any mainstream-ish
language with objects mutates them.
I didn't say that you could translate JS programs. I said that it was
similar.
And JS is, according to several surveys, the world's most commonly-used
programming language, making it familiar to more people than any other
option.
Like JS's mechanism, calling shadowed parent methods is ...
interesting. (I should note that for work-legacy reasons almost all
of my JS experience is with 2006-era JS.) I have two thoughts on the
subject. First, when you clone the original object, you can copy a
method reference into your own name, preserving access to it:
o1 = object(method=function (arg) ... );
o2 = object(o1, superMethod=o1.method, method=function (arg) this.superMethod(arg) + 1);
This is definitely not simple.
And if I thought that we were going to have most users building class
hierarchies, that would bother me. But we're not.
My expectation is that most users will never consciously create an
object with methods.
Doing something like Function.call() is in many ways cleaner, and
perhaps we should eventually do that.
>> As they stand today, they are indeed better mapped to Python
>> dictionaries. We could call them dictionaries, records, or any of a
>> dozen other data-only names. But then when and if we add more
>> OO-like features, starting with Peter's work on a "this" mechanism,
>> we would be stuck forever with a name that is inappropriate...
>>
> My suggestion is that we should not add any OO-like features to the
> key-value store unless we've thought through the design.
Should we back out the existing features, because they are "incomplete"?
Should we rename the data type, and preserve that different name forever?
> Doing things incrementally is exactly how you get "the complexity of
> most OO languages". It's exactly why JavaScript has many different
> ways to create objects, why C++ has new and delete and std::unique_ptr
> and std::shared_ptr and multiple inheritance and virtual inheritance
> and...
And if we try to do everything, that's what we'll get. I think that
`this` is close to the limit of what we want to implement; I'm not sure
that a `super` mechanism makes the cut, especially if it is clearly
possible to DIY.
>> As for familiarity and similarity to other languages... What I've
>> called objects in OpenSCAD are patterned after JavaScript objects.
>> Their access mechanisms are nearly identical to JS objects.[1] With
>> the OEP8 syntax (PR #6081), the creation syntax is a superset of JS's
>> syntax.[2][3]
>>
> You can't straightforwardly translate almost any actual JavaScript
> code to OpenSCAD because it mutates objects. I think that makes it a
> poor example to copy.
Depending on what you mean by "mutates objects", any mainstream-ish
language with objects mutates them.
I didn't say that you could translate JS programs. I said that it was
similar.
And JS is, according to several surveys, the world's most commonly-used
programming language, making it familiar to more people than any other
option.
>> Like JS's mechanism, calling shadowed parent methods is ...
>> interesting. (I should note that for work-legacy reasons almost all
>> of my JS experience is with 2006-era JS.) I have two thoughts on the
>> subject. First, when you clone the original object, you can copy a
>> method reference into your own name, preserving access to it:
>>
>> o1 = object(method=function (arg) ... );
>> o2 = object(o1, superMethod=o1.method, method=function (arg) this.superMethod(arg) + 1);
>>
> This is definitely not simple.
And if I thought that we were going to have most users building class
hierarchies, that would bother me. But we're not.
My expectation is that most users will never consciously create an
object with methods.
Doing something like Function.call() is in many ways cleaner, and
perhaps we should eventually do that.
CC
Cory Cross
Mon, Aug 25, 2025 12:23 AM
On 8/24/25 4:17 PM, Jordan Brown via Discuss wrote:
As they stand today, they are indeed better mapped to Python
dictionaries. We could call them dictionaries, records, or any of a
dozen other data-only names. But then when and if we add more
OO-like features, starting with Peter's work on a "this" mechanism,
we would be stuck forever with a name that is inappropriate...
My suggestion is that we should not add any OO-like features to the
key-value store unless we've thought through the design.
Should we rename the data type, and preserve that different name forever?
As for familiarity and similarity to other languages... What I've
called objects in OpenSCAD are patterned after JavaScript objects.
Their access mechanisms are nearly identical to JS objects.[1] With
the OEP8 syntax (PR #6081), the creation syntax is a superset of
JS's syntax.[2][3]
You can't straightforwardly translate almost any actual JavaScript
code to OpenSCAD because it mutates objects. I think that makes it a
poor example to copy.
Depending on what you mean by "mutates objects", any mainstream-ish
language with objects mutates them.
I didn't say that you could translate JS programs. I said that it was
similar.
And JS is, according to several surveys, the world's most
commonly-used programming language, making it familiar to more people
than any other option.
Except that familiarity is instead misleading and confusing, because you
can't do the majority of things you actually do in JavaScript with the
proposed OpenSCAD object.
This is definitely not simple.
And if I thought that we were going to have most users building class
hierarchies, that would bother me. But we're not.
My expectation is that most users will never consciously create an
object with methods.
Doing something like Function.call() is in many ways cleaner, and
perhaps we should eventually do that.
It seems your definition of "simple" only applies to neophyte users of
OpenSCAD and isn't intended to make it simpler for power users. I'd like
it to be simple for both.
/back to C++
On 8/24/25 4:17 PM, Jordan Brown via Discuss wrote:
>>>
>>> As they stand today, they are indeed better mapped to Python
>>> dictionaries. We could call them dictionaries, records, or any of a
>>> dozen other data-only names. But then when and if we add more
>>> OO-like features, starting with Peter's work on a "this" mechanism,
>>> we would be stuck forever with a name that is inappropriate...
>>>
>> My suggestion is that we should not add any OO-like features to the
>> key-value store unless we've thought through the design.
>
> Should we rename the data type, and preserve that different name forever?
>
JavaScript objects have many pitfalls when used as a key-value store
<https://web.archive.org/web/20150729173703/http://speakingjs.com/es5/ch17.html#_pitfalls_using_an_object_as_a_map>.,
so anyone used to JavaScript in the last decade would be used to Map
<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map>.
So, yes, use a proper name for key-value store if that's the primary
goal. And let Objects be for Object-Oriented programming.
>>> As for familiarity and similarity to other languages... What I've
>>> called objects in OpenSCAD are patterned after JavaScript objects.
>>> Their access mechanisms are nearly identical to JS objects.[1] With
>>> the OEP8 syntax (PR #6081), the creation syntax is a superset of
>>> JS's syntax.[2][3]
>>>
>> You can't straightforwardly translate almost any actual JavaScript
>> code to OpenSCAD because it mutates objects. I think that makes it a
>> poor example to copy.
>
> Depending on what you mean by "mutates objects", any mainstream-ish
> language with objects mutates them.
>
> I didn't say that you could translate JS programs. I said that it was
> similar.
>
> And JS is, according to several surveys, the world's most
> commonly-used programming language, making it familiar to more people
> than any other option.
>
Except that familiarity is instead misleading and confusing, because you
can't do the majority of things you actually do in JavaScript with the
proposed OpenSCAD object.
>> This is definitely not simple.
>
> And if I thought that we were going to have most users building class
> hierarchies, that would bother me. But we're not.
>
> My expectation is that most users will never consciously create an
> object with methods.
>
> Doing something like Function.call() is in many ways cleaner, and
> perhaps we should eventually do that.
>
It seems your definition of "simple" only applies to neophyte users of
OpenSCAD and isn't intended to make it simpler for power users. I'd like
it to be simple for both.
/back to C++
- Cory