discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

new docs for Objects and object() function

V
vulcan_@mac.com
Wed, Aug 6, 2025 8:11 AM

i have used the github wiki page and jordan’s input to craft a new document for objects

i hope this helps

.. in case that link does is not visible https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Objects

feedback is welcome

i have used the github wiki page and jordan’s input to [craft a new document for objects](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Objects) i hope this helps .. in case that link does is not visible https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Objects feedback is welcome
JB
Jordan Brown
Fri, Aug 8, 2025 1:34 PM
   Built-In Object Creators

textmetrics()
function to present data from the text in a string
fontmetrics()
function to present data from a font.
data=import()
function to import date from a JSON file.

I would delete this section.  There are functions that return objects,
just as there are functions that return numbers, strings, vectors, et
cetera.  Do we have a "functions that return numbers" section anywhere,
that would include len() and sin()?

At the most, I would put it at the bottom as a "See Also" section.

   Object() Function

[Note:Requires version2025.07.11]

Should be "Development snapshot".  2025.07.11 isn't a release.

Note: in this section/element/and/member/are used interchangeably
for the data items of an|object|

Be consistent.  I'm sure there's lots of terminological inconsistency
throughout the manual, but we shouldn't be adding more.  I don't have a
preference between these two.  I would look at what JavaScript and
Python materials call their equivalents.

"object" here should not be in typewriter font, because in this sentence
it's not a keyword.

It seems like this section should start with an overview of what objects
are: that they are collections of values, accessed by name.

   Member Names

Member names are strings that may includeany valid character
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Characters_Strings

You don't need to go past "are strings", because strings by definition
can only include valid characters.

invalidname="invalid\u00A3object,name";
goodname="goodname";
xxx=object([
["bad name",12],
[invalidname,"invalid"],
[goodname,"good"],
["this\nthat","value"]
]
);

This is misleading, because all of those are valid object member names.

What's different between goodname and the others is that goodname can
use the "short cut" forms:  you can set it using
object(goodname="good"), and you can access it with o.goodname. For the
others you have to use object() with its vector-form argument, and you
have to use list-style o["this\nthat"] form.

I wouldn't dive right into what is perhaps the most complex piece of the
puzzle, list-style arguments to object().

I would probably start with the two access forms, and the distinctions
between them:

  • Dot form can only be used for name-safe strings.  Bracket form can
    be used for all strings.
  • Dot form can only be used for constant strings.  Bracket form can be
    used where the string is the result of an expression.

I would perhaps give one example of a name that is name-safe, and one
that is not, and the one that is not name-safe would not be exotic.  It
would contain a space or perhaps a punctuation mark.

Thus any member name may be used as as string in the syntax of the
object() function or to use the member in an expression.

This sentence doesn't make any sense to me.

"thus" implies a preceding statement that leads to a conclusion, and
that doesn't seem the case here.

object() doesn't have any unique syntax.  It has various arguments
that it accepts.

"to use the member in an expression" doesn't mean much, given that we
haven't discussed how to access members.

When a member name also meets the requirements ofNamed Objects
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objectsit
may be used in the name=value form of member definition and for
accessing object members using Dot Notation.

Except that we haven't talked about Dot Notation.

   Name=Value Members

The simplest definition is a|name=value|assignment:

my_object = object( <name=value>* );

where name is a valid variable nameper the language rules
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objectsand
value is any valid expression result.

The documentation is not terribly consistent, but this pseudo-BNF form
is not very common.  We should be more consistent.

When using this form, the argument would be <name>=<value>.

We would need to have a clear convention that <foo>* refers to a
comma-separated list of foos.

the "my_object=" part is not part of the description of object(), so is
questionable.

thus

Fred = object( name="Fred", spouse="Wilma", friends=["Barney"] );
// ECHO: Fred = { name = "Fred"; spouse = "wilma"; friend = ["barney"]; }

Gives an echo result, but doesn't have an echo().

object is created with "Wilma", "friends", and "Barney", but the output
reported has "wilma", "friend", and "barney".

When the value is the result of an expression or function call but
is|undef|the member is still added:

Barney = object( name="Barney", spouse=Betty, friends=["Fred"] );
// ECHO: Barney = { name = "Barney"; spouse = undef; friend = ["Fred"]; }

This is an error case (undefined variable) and so we needn't say much
about it - and in particular we don't have to say anything special here,
because the behavior is completely standard.  The value resulting from
referring to an undefined variable is undef, here and everywhere.

   Vector of Members

A bulk definition of elements may be accomplished by giving a vector
of|[name,value]|vector elements

I wouldn't say "bulk", because that's not the only reason for using this
form.

my_object = object( [ ["<name>",<value>]* ] );

Again, the "my_object =" part is questionable.

<name> should not be in quotes; there is no requirement that it be a
constant string.

(This does lead to a conflict:  when does <name> mean a constant name,
and when does it mean an expression that evaluates to a string?  Perhaps
<name> means a constant name, and <name-expr> or <name-string> means an
expression that evaluates to a string.)

Where the|<name>|is any expression resulting in a|string|per therules
of Member Names
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Objects#Member_Names,

There are no rules.  Any string is a valid key.

 and the value is any valid expression, lambda function, or object.

function literals and objects are expressions.  Stop right after
"expression".

"valid" goes without saying.

But it is more correct to say that a|vector argument|is for performing
bulk/editing/of the object's elements.

No, that is not at all correct.  Any of the three forms can be used for
adding and updating members, and any of the three can be used to create
objects from zero.  The only editing aspect that is unique to the vector
form is deletion.

It is possible to/remove/a member by including a/name only/element
like,|[["name"]]|, and is not an error to attempt removing a member
that does not exist in the object being copied.

Assigning|undef|is allowed, as seen above, but that does not remove
the member.

undef is just a value, with almost no special semantics.  At most this
should be a note; it does not say anything unique about the behavior but
rather cautions against a potential misunderstanding.

It is not clear to me whether cautioning about a potential
misunderstanding belongs in the reference section - but if it does, it
should be clearly marked.

   Example of Removal

We define a person:

Fred = object( name="Fred", spouse="Wilma", friends=["Barney"] );

but we want the spouse to be an object, not just the name as as
string, so later we remove it:

"we want the spouse to be an object" is not helpful here.  (If we just
want to change it, why aren't we just changing it?)

Also, the data structure that you're trying to represent cannot be
represented, because there are no references.  You cannot have a pair of
objects A and B, with A.spouse == B and B.spouse == A.

newFred = object( Fred, [["spouse"]] );
//ECHO: { name = "Fred"; friends = ["barney"]; }

The spouse member is gone.

This example is more or less OK, except for the comments above.

When a member named for removal does not exist in the object there is
no error and no change in the object. So from this:|newFred = object(
Fred, [["child"]] );|the new object, newFred, is an exact copy of Fred
as there is no "child" member.

You already said that the member didn't need to exist; it isn't
necessary to repeat it.

   Object as Member

Title is wrong.  You are not describing an object as a member. You are
describing creating one object based on another.

An existing object may be given as an argument to an|object()|:

my_object = object( "one"=1 ); // make an object

Syntactically incorrect; "one" should not be in quotes.

your_object = object( my_object ); // make a copy

Additional members may be added to the copy, and removals done, by
giving them as parameters.

Lets assume that Fred and Wilma objects are already defined. Wilma
with a member|spouse=Fred|and having removed spouse from Fred we want
to add the Wilma object as spouse:

newFred = object( Fred, spouse=Wilma );

Fine example, but...

and now:

newFred = {
name = "Fred";
spouse = {
name = "Wilma"; // object as spouse
spouse = {
name = "Fred"; // the Fred Object is Wilma's spouse
spouse = "wilma"; // placeholder value
friends = ["barney"]; // Fred's friend
};
friends = ["Betty"]; // Wilma's friend
};
friends = ["barney"]; // newFred's friend
}

Now you're down a rabbit hole, because you're trying to create a graph
with a loop, and you can't, and the result is just confusing.

Now we can chain member references ject in an object:

"ject" ?

echo( WHsp = Wilma.spouse.spouse, "\n" ); // "wilma" from original Fred

Lose the newline; it's not helpful and just adds noise.

The argument list of|object()|may combine any of the member definition
forms allowing us to combine operations. The|spouse|member could be
removed and replaced in one call:

newFred = object( Fred, [["spouse"]], spouse=Wilma );
echo( newFred =newFred, "\n");

Yes, except that deleting the member before updating it is not necessary.

Also:  lose the newline, and remove the space before the equals sign in
the echo().

gives

newFred = {
name = "Fred";
friends = ["barney"];
spouse = { name = "Wilma"; spouse = undef; friends = ["Betty"]; };
},

You never created a variable named Wilma, making the fact that
Wilma.spouse==undef be rather unexpected.

Showing the|spouse|member is now the|Wilma|object and is the last
member in the object.

It isn't the Wilma object.  It is a copy of Wilma.  There are no
references.

Note: The order of the members is updated by the removal a member
and by the addition of a new one

There hasn't been any discussion of order yet, so this seems out of place.

   Object Combinations Accumulate

Giving an object as an argument copies its members to the new object.
Giving additional objects as arguments will add its members to the new
object, overriding any members of the first object that have the same
name.

The "override earlier values" semantic is not unique to supplying
objects as arguments; it applies to all three forms.

The language rule about assigning a new value to an existing variable
applies to object creation.

It's not really a language rule.  It's a rule for the arguments
processed by the object() function.

And yes, it applies to object creation... where else would it apply?

The rule is that assignment to a previously defined name sets the new
value at the location of the first definition meaning that the new
value is used everywhere in the script.

This seems to be mixing object construction with variable scoping, and
they are not at all the same thing.

Any discussion of order needs to start out with a statement that members
are reported out in the order that they were added.

The arguments to object() are effectively a sequence of assignments
creating named objects and so any of the member definition forms that
use existing member names will override previous values, and when
listed ( by echo() ) will show the last set value in the position of
the first definition.

This sentence needs help.

When we add two objects to create xxx:

Fred=object(name="Fred",spouse="wilma",friends=["Barney"]);
echo(Fred=Fred,"\n");
// define Wilma using the Fred object as spouse
Wilma=object(name="Wilma",spouse=Fred,friends=["Betty"]);
echo(Wilma=Wilma,"\n");
xxx=object(Fred,Wilma);

The combined object, xxx, will be a copy of Wilma:

... making it an uninteresting example.

Members|name|and|spouse|from|Wilma|have overridden those of|Fred|, and
the|children|and|pets|vectors are added in the order
of|Fred|and|Wilma|in the argument list.

To illustrate this further we combine the original Fred and Wilma,
then remove the spouse member:

This has been a lot of words about order, when it can be summarized
crisply in two lines:

  • Members are added in the order supplied.
  • Replacing a previously-added member does not move it.

and maybe a third:

  • Once you remove a member it is no longer present, and so re-adding
    it adds it at the end.
  1. Fred object is added to xxx
  2. members of the Wilma object override the values of Fred's members
  3. removing the|spouse|member affects the copy of Wilma
  4. xxx is a copy of Wilma, with no spouse

Way too complex an example.  (And again, this is a bad data structure to
discuss because it cannot be represented correctly.)

If an assignment or object later in the argument list overrides
something before it it may look like members are out of order. If we
prefer to have Fred.spouse be the Wilma object we might try:

// set spouse to a placeholder value
Fred=object(name="Fred",spouse="wilma",friends=["barney"]);
// create a Wilma object
Wilma=object(name="Wilma",spouse=Fred,friends=["Betty"]);
// this will NOT give Fred2 the Wilma object as spouse
Fred2=object(spouse=Wilma,Fred);
// ECHO: { spouse = "wilma"; name = "Fred"; friends = ["barney"]; }

To explain:

  1. The new object,|Fred2|, is given the|Wilma|object as its first
    member,|spouse|
  2. the|Fred|object is added so its members are applied:
    1. |Fred.spouse|("wilma") overrides Fred2.spouse in the position
      of the first definition in Fred2
    2. Fred.name and Fred.friends are added to Fred2 as second and
      third members

The result shown by echo() by the rule of "new value defined as if at
original definition" is correct.

Way too complicated; delete all of it.

   Functions as Members

Need not be mentioned.  Function references are values, and members can
be any value.  We do not, and should not, explicitly say that members
can be numbers, strings, booleans, lists, ranges, undef, functions,
objects, et cetera.

 Object Data Type

[Note:Requires versionDevelopment snapshot]

An "object" is a collection of names and associated values. In other
languages this data structure might be called an object (JavaScript),
a dictionary (Python), or an associative array (some UNIX shells, awk),

This seems like it belongs at the top.

   Retrieving Member Values

The name of a member may be used in this dot notation:

n = obj.name;

Give the valid-identifier restriction right here.

Using the vector index form, but with a string required in place of an
integer, we can achieve the same result:

n=obj["name"];

Members withvalid identifier names
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objectscan
be accessed using either syntax. Names for members that do not meet
theNamed Object rules
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objectsmust
be given as strings using this format.

To a certain extent you've broadened the context back to both forms, and
so in the last phrase "this" is not well-bound.

There should really only be one link to the name rules, and there should
really only be two places that mention it at all: object()'s name=value
style, and the "dot notation" access form.

Somewhere in this "access" section it should say that attempting to
access a member that is not present is not an error, and yields undef.

   Iterating over object members

The for statement is extended [...]

This is a description of the language as it is, not a description of a
change to the language.  It should just say "The for statement loops
through ...".

to loop through the keys of an object in the order the object members
were defined in:

Order need to be defined in one place only.

for( key = object ) <statement object[key]>

The loop counter|key|in this case, is the member's name as a string.

Needs a comma after "counter".

Since member names are always strings, it's not necessary to say "as a
string".

"<statement object[key]>" doesn't really mean anything.

Better would probably be

for (key = object) ... object[key] ...

Inside the loop the array index form, ["key"],

Quotes are wrong, and this probably should say "object[key]".

 must be used to refer to members,

"must" isn't really right, for two reasons.

  • You don't have to refer to members.  It's a bit weird not to, but
    there are valid use cases.
  • It's perfectly legal to refer to members using dot notation. You
    just can't refer to the particular member that you're iterating past
    that way.

 with the advantage that even keys with non-compliant names will be
correctly processed.

Unnecessary; we've already discussed the semantics of o[k] style access.

So:

oo = object( [["a!",1],["b@",2],["c#",3],["d$",4],["e%",5],["f^",6] ] );
keys = [for( io = oo ) io]; //  ["a!", "b@", "c#", "d$", "e%", "f^"]

Unnecessary complexity.

To be able to process all of the object's values we must use the text
names:

doubles = object( [ for( io = oo ) [ io, oo[io] * 2 ] ] );
echo(doubles);  // [2, 4, 6, 8, 10, 12]

The lead-in comment doesn't have anything obvious to do with the example.

The actual result, given the previous definition of oo, is much uglier
and more complex than desirable, both because there are so many members
and because they have ugly names.

If I understand the intent correctly, it is to show iteration over an
object's keys.  This example unnecessarily throws in list comprehension
and creating a new object.  Better would be something like:

for (io = oo) echo(io, oo[io]*2);
 Object Comparisons

The equality operators work with objects:

Great.  Describe their rules.

But not the relational ones:

xx_object=object(name="this",other=12);
yy_object=object(name="this",other=13);

echo(xx_object<yy_object);// WARNING: operation undefined (object <
object)
echo(xx_object>=yy_object);// WARNING: operation undefined (object >=
object)

Saying that relational operators are not defined for objects is enough;
it is not necessary to give examples.

 has_key() Function

There is a new function has_key( <object>,<name> ) that returns true
if the object contains the named key.

Again, we are describing the language as it is; we are not describing a
change.  Just say:

The function has_key(<object>, <name>) returns true if the object
contains the named key, and false otherwise.
> > Built-In Object Creators > > textmetrics() > function to present data from the text in a string > fontmetrics() > function to present data from a font. > data=import() > function to import date from a JSON file. > I would delete this section.  There are functions that return objects, just as there are functions that return numbers, strings, vectors, et cetera.  Do we have a "functions that return numbers" section anywhere, that would include len() and sin()? At the most, I would put it at the bottom as a "See Also" section. > > Object() Function > > [Note:Requires version2025.07.11] > Should be "Development snapshot".  2025.07.11 isn't a release. > *Note*: in this section/element/and/member/are used interchangeably > for the data items of an|object| > Be consistent.  I'm sure there's lots of terminological inconsistency throughout the manual, but we shouldn't be adding more.  I don't have a preference between these two.  I would look at what JavaScript and Python materials call their equivalents. "object" here should not be in typewriter font, because in this sentence it's not a keyword. It seems like this section should start with an overview of what objects are: that they are collections of values, accessed by name. > Member Names > > Member names are strings that may includeany valid character > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Characters_Strings> > You don't need to go past "are strings", because strings by definition can only include valid characters. > invalidname="invalid\u00A3object,name"; > goodname="goodname"; > xxx=object([ > ["bad name",12], > [invalidname,"invalid"], > [goodname,"good"], > ["this\nthat","value"] > ] > ); This is misleading, because all of those are valid object member names. What's different between goodname and the others is that goodname can use the "short cut" forms:  you can set it using object(goodname="good"), and you can access it with o.goodname. For the others you have to use object() with its vector-form argument, and you have to use list-style o["this\nthat"] form. I wouldn't dive right into what is perhaps the most complex piece of the puzzle, list-style arguments to object(). I would probably start with the two access forms, and the distinctions between them: * Dot form can only be used for name-safe strings.  Bracket form can be used for all strings. * Dot form can only be used for constant strings.  Bracket form can be used where the string is the result of an expression. I would perhaps give one example of a name that is name-safe, and one that is not, and the one that is not name-safe would not be exotic.  It would contain a space or perhaps a punctuation mark. > Thus any member name may be used as as string in the syntax of the > object() function or to use the member in an expression. > This sentence doesn't make any sense to me. "thus" implies a preceding statement that leads to a conclusion, and that doesn't seem the case here. object() doesn't have any unique syntax.  It has various *arguments* that it accepts. "to use the member in an expression" doesn't mean much, given that we haven't discussed how to access members. > When a member name also meets the requirements ofNamed Objects > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objects>it > may be used in the name=value form of member definition and for > accessing object members using Dot Notation. > Except that we haven't talked about Dot Notation. > Name=Value Members > > The simplest definition is a|name=value|assignment: > > my_object = object( <name=value>* ); > > where name is a valid variable nameper the language rules > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objects>and > value is any valid expression result. > The documentation is not terribly consistent, but this pseudo-BNF form is not very common.  We should be more consistent. When using this form, the argument would be <name>=<value>. We would need to have a clear convention that <foo>* refers to a comma-separated list of foos. the "my_object=" part is not part of the description of object(), so is questionable. > thus > > Fred = object( name="Fred", spouse="Wilma", friends=["Barney"] ); > // ECHO: Fred = { name = "Fred"; spouse = "wilma"; friend = ["barney"]; } Gives an echo result, but doesn't have an echo(). object is created with "Wilma", "friends", and "Barney", but the output reported has "wilma", "friend", and "barney". > When the value is the result of an expression or function call but > is|undef|the member is still added: > > Barney = object( name="Barney", spouse=Betty, friends=["Fred"] ); > // ECHO: Barney = { name = "Barney"; spouse = undef; friend = ["Fred"]; } This is an error case (undefined variable) and so we needn't say much about it - and in particular we don't have to say anything special here, because the behavior is completely standard.  The value resulting from referring to an undefined variable is undef, here and everywhere. > Vector of Members > > A bulk definition of elements may be accomplished by giving a vector > of|[name,value]|vector elements > I wouldn't say "bulk", because that's not the only reason for using this form. > my_object = object( [ ["<name>",<value>]* ] ); Again, the "my_object =" part is questionable. <name> should not be in quotes; there is no requirement that it be a constant string. (This does lead to a conflict:  when does <name> mean a constant name, and when does it mean an expression that evaluates to a string?  Perhaps <name> means a constant name, and <name-expr> or <name-string> means an expression that evaluates to a string.) > Where the|<name>|is any expression resulting in a|string|per therules > of Member Names > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Objects#Member_Names>, > There are no rules.  Any string is a valid key. >  and the value is any valid expression, lambda function, or object. > function literals and objects *are* expressions.  Stop right after "expression". "valid" goes without saying. > But it is more correct to say that a|vector argument|is for performing > bulk/editing/of the object's elements. > No, that is not at all correct.  Any of the three forms can be used for adding and updating members, and any of the three can be used to create objects from zero.  The only editing aspect that is unique to the vector form is deletion. > It is possible to/remove/a member by including a/name only/element > like,|[["name"]]|, and is not an error to attempt removing a member > that does not exist in the object being copied. > > Assigning|undef|is allowed, as seen above, but that does not remove > the member. > undef is just a value, with almost no special semantics.  At most this should be a note; it does not say anything unique about the behavior but rather cautions against a potential misunderstanding. It is not clear to me whether cautioning about a potential misunderstanding belongs in the reference section - but if it does, it should be clearly marked. > Example of Removal > > We define a person: > > Fred = object( name="Fred", spouse="Wilma", friends=["Barney"] ); > > but we want the spouse to be an object, not just the name as as > string, so later we remove it: > "we want the spouse to be an object" is not helpful here.  (If we just want to change it, why aren't we just changing it?) Also, the data structure that you're trying to represent cannot be represented, because there are no references.  You cannot have a pair of objects A and B, with A.spouse == B and B.spouse == A. > newFred = object( Fred, [["spouse"]] ); > //ECHO: { name = "Fred"; friends = ["barney"]; } > > The spouse member is gone. > This example is more or less OK, except for the comments above. > When a member named for removal does not exist in the object there is > no error and no change in the object. So from this:|newFred = object( > Fred, [["child"]] );|the new object, newFred, is an exact copy of Fred > as there is no "child" member. > You already said that the member didn't need to exist; it isn't necessary to repeat it. > Object as Member > Title is wrong.  You are not describing an object as a member. You are describing creating one object based on another. > An existing object may be given as an argument to an|object()|: > > my_object = object( "one"=1 ); // make an object Syntactically incorrect; "one" should not be in quotes. > your_object = object( my_object ); // make a copy > > Additional members may be added to the copy, and removals done, by > giving them as parameters. > > Lets assume that Fred and Wilma objects are already defined. Wilma > with a member|spouse=Fred|and having removed spouse from Fred we want > to add the Wilma object as spouse: > > newFred = object( Fred, spouse=Wilma ); Fine example, but... > and now: > > newFred = { > name = "Fred"; > spouse = { > name = "Wilma"; // object as spouse > spouse = { > name = "Fred"; // the Fred Object is Wilma's spouse > spouse = "wilma"; // placeholder value > friends = ["barney"]; // Fred's friend > }; > friends = ["Betty"]; // Wilma's friend > }; > friends = ["barney"]; // newFred's friend > } Now you're down a rabbit hole, because you're trying to create a graph with a loop, and you can't, and the result is just confusing. > Now we can chain member references ject in an object: > "ject" ? > echo( WHsp = Wilma.spouse.spouse, "\n" ); // "wilma" from original Fred Lose the newline; it's not helpful and just adds noise. > The argument list of|object()|may combine any of the member definition > forms allowing us to combine operations. The|spouse|member could be > removed and replaced in one call: > > newFred = object( Fred, [["spouse"]], spouse=Wilma ); > echo( newFred =newFred, "\n"); Yes, except that deleting the member before updating it is not necessary. Also:  lose the newline, and remove the space before the equals sign in the echo(). > gives > > newFred = { > name = "Fred"; > friends = ["barney"]; > spouse = { name = "Wilma"; spouse = undef; friends = ["Betty"]; }; > }, You never created a variable named Wilma, making the fact that Wilma.spouse==undef be rather unexpected. > Showing the|spouse|member is now the|Wilma|object and is the last > member in the object. > It isn't *the* Wilma object.  It is a copy of Wilma.  There are no references. > *Note*: The order of the members is updated by the removal a member > and by the addition of a new one > There hasn't been any discussion of order yet, so this seems out of place. > Object Combinations Accumulate > > Giving an object as an argument copies its members to the new object. > Giving additional objects as arguments will add its members to the new > object, overriding any members of the first object that have the same > name. > The "override earlier values" semantic is not unique to supplying objects as arguments; it applies to all three forms. > The language rule about assigning a new value to an existing variable > applies to object creation. > It's not really a language rule.  It's a rule for the arguments processed by the object() function. And yes, it applies to object creation... where else would it apply? > The rule is that assignment to a previously defined name sets the new > value at the location of the first definition meaning that the new > value is used everywhere in the script. > This seems to be mixing object construction with variable scoping, and they are not at all the same thing. Any discussion of order needs to start out with a statement that members are reported out in the order that they were added. > The arguments to object() are effectively a sequence of assignments > creating named objects and so any of the member definition forms that > use existing member names will override previous values, and when > listed ( by echo() ) will show the last set value in the position of > the first definition. > This sentence needs help. > When we add two objects to create xxx: > > Fred=object(name="Fred",spouse="wilma",friends=["Barney"]); > echo(Fred=Fred,"\n"); > // define Wilma using the Fred object as spouse > Wilma=object(name="Wilma",spouse=Fred,friends=["Betty"]); > echo(Wilma=Wilma,"\n"); > xxx=object(Fred,Wilma); > > The combined object, xxx, will be a copy of Wilma: > ... making it an uninteresting example. > Members|name|and|spouse|from|Wilma|have overridden those of|Fred|, and > the|children|and|pets|vectors are added in the order > of|Fred|and|Wilma|in the argument list. > > To illustrate this further we combine the original Fred and Wilma, > then remove the spouse member: > This has been a lot of words about order, when it can be summarized crisply in two lines: * Members are added in the order supplied. * Replacing a previously-added member does not move it. and maybe a third: * Once you remove a member it is no longer present, and so re-adding it adds it at the end. > 1. Fred object is added to xxx > 2. members of the Wilma object override the values of Fred's members > 3. removing the|spouse|member affects the copy of Wilma > 4. xxx is a copy of Wilma, with no spouse > Way too complex an example.  (And again, this is a bad data structure to discuss because it cannot be represented correctly.) > If an assignment or object later in the argument list overrides > something before it it may look like members are out of order. If we > prefer to have Fred.spouse be the Wilma object we might try: > > // set spouse to a placeholder value > Fred=object(name="Fred",spouse="wilma",friends=["barney"]); > // create a Wilma object > Wilma=object(name="Wilma",spouse=Fred,friends=["Betty"]); > // this will NOT give Fred2 the Wilma object as spouse > Fred2=object(spouse=Wilma,Fred); > // ECHO: { spouse = "wilma"; name = "Fred"; friends = ["barney"]; } > > To explain: > > 1. The new object,|Fred2|, is given the|Wilma|object as its first > member,|spouse| > 2. the|Fred|object is added so its members are applied: > 1. |Fred.spouse|("wilma") overrides Fred2.spouse in the position > of the first definition in Fred2 > 2. Fred.name and Fred.friends are added to Fred2 as second and > third members > > The result shown by echo() by the rule of "new value defined as if at > original definition" is correct. > Way too complicated; delete all of it. > Functions as Members > Need not be mentioned.  Function references are values, and members can be any value.  We do not, and should not, explicitly say that members can be numbers, strings, booleans, lists, ranges, undef, functions, objects, et cetera. > Object Data Type > > [Note:Requires versionDevelopment snapshot] > > An "object" is a collection of names and associated values. In other > languages this data structure might be called an object (JavaScript), > a dictionary (Python), or an associative array (some UNIX shells, awk), > This seems like it belongs at the top. > Retrieving Member Values > > The name of a member may be used in this dot notation: > > n = obj.name; Give the valid-identifier restriction right here. > Using the vector index form, but with a string required in place of an > integer, we can achieve the same result: > > n=obj["name"]; > > Members withvalid identifier names > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objects>can > be accessed using either syntax. Names for members that do not meet > theNamed Object rules > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Named_Objects>must > be given as strings using this format. > To a certain extent you've broadened the context back to both forms, and so in the last phrase "this" is not well-bound. There should really only be one link to the name rules, and there should really only be two places that mention it at all: object()'s name=value style, and the "dot notation" access form. Somewhere in this "access" section it should say that attempting to access a member that is not present is not an error, and yields undef. > Iterating over object members > > The for statement is extended [...] > This is a description of the language as it is, not a description of a change to the language.  It should just say "The for statement loops through ...". > to loop through the keys of an object in the order the object members > were defined in: > Order need to be defined in one place only. > for( key = object ) <statement object[key]> > > The loop counter|key|in this case, is the member's name as a string. > Needs a comma after "counter". Since member names are always strings, it's not necessary to say "as a string". "<statement object[key]>" doesn't really mean anything. Better would probably be for (key = object) ... object[key] ... > Inside the loop the array index form, ["key"], > Quotes are wrong, and this probably should say "object[key]". >  must be used to refer to members, > "must" isn't really right, for two reasons. * You don't have to refer to members.  It's a bit weird not to, but there are valid use cases. * It's perfectly legal to refer to members using dot notation. You just can't refer to the particular member that you're iterating past that way. >  with the advantage that even keys with non-compliant names will be > correctly processed. > Unnecessary; we've already discussed the semantics of o[k] style access. > So: > > oo = object( [["a!",1],["b@",2],["c#",3],["d$",4],["e%",5],["f^",6] ] ); > keys = [for( io = oo ) io]; // ["a!", "b@", "c#", "d$", "e%", "f^"] Unnecessary complexity. > To be able to process all of the object's values we must use the text > names: > > doubles = object( [ for( io = oo ) [ io, oo[io] * 2 ] ] ); > echo(doubles); // [2, 4, 6, 8, 10, 12] The lead-in comment doesn't have anything obvious to do with the example. The actual result, given the previous definition of oo, is much uglier and more complex than desirable, both because there are so many members and because they have ugly names. If I understand the intent correctly, it is to show iteration over an object's keys.  This example unnecessarily throws in list comprehension and creating a new object.  Better would be something like: for (io = oo) echo(io, oo[io]*2); > > Object Comparisons > > The equality operators work with objects: > Great.  Describe their rules. > But not the relational ones: > > xx_object=object(name="this",other=12); > yy_object=object(name="this",other=13); > > echo(xx_object<yy_object);// WARNING: operation undefined (object < > object) > echo(xx_object>=yy_object);// WARNING: operation undefined (object >= > object) Saying that relational operators are not defined for objects is enough; it is not necessary to give examples. > has_key() Function > > There is a new function has_key( <object>,<name> ) that returns true > if the object contains the named key. > Again, we are describing the language as it is; we are not describing a change.  Just say: The function has_key(<object>, <name>) returns true if the object contains the named key, and false otherwise.
V
vulcan_@mac.com
Fri, Aug 8, 2025 3:19 PM

Jordan Brown wrote:

  Built-In Object Creators

textmetrics()
function to present data from the text in a string
fontmetrics()
function to present data from a font.
data=import()
function to import date from a JSON file.

I would delete this section.  There are functions that return objects,
just as there are functions that return numbers, strings, vectors, et
cetera.  Do we have a "functions that return numbers" section anywhere,
that would include len() and sin()?

I am only going to reply to this one point as dinner is about to land on the table:

i am using this existing page https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Mathematical_Functions

to reorg the built-in functions, eventually to be replaced by a new section Built-In Functions

Misc functions that take one type and give another, but that don’t obviously fit in logical, relational, and type_checking .. like len, let, lookup etc

the Generator list is for rands() as it generates a vector of values meet a condition of randomness

I expect to add Metrics section for FontMetrics() and Textmetrics() .. i feel the nature of the info they provide is more important than the fact they provide it as objects

the existing Math Functions section is for the functions that take and return numbers

ob this is a WIP .. i will start in on the rest of your feedback tomorrow probably .. my daughter is coming for dinner

Jordan Brown wrote: > > ``` > > Built-In Object Creators > > ``` > > > > textmetrics() > > function to present data from the text in a string > > fontmetrics() > > function to present data from a font. > > data=import() > > function to import date from a JSON file. > > I would delete this section.  There are functions that return objects, > just as there are functions that return numbers, strings, vectors, et > cetera.  Do we have a "functions that return numbers" section anywhere, > that would include len() and sin()? I am only going to reply to this one point as dinner is about to land on the table: i am using this existing page https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Mathematical_Functions to reorg the built-in functions, eventually to be replaced by a new section [Built-In Functions](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_of_Builtin_Functions) Misc functions that take one type and give another, but that don’t obviously fit in logical, relational, and type_checking .. like len, let, lookup etc the Generator list is for rands() as it generates a vector of values meet a condition of randomness I expect to add Metrics section for FontMetrics() and Textmetrics() .. i feel the nature of the info they provide is more important than the fact they provide it as objects the existing Math Functions section is for the functions that take and return numbers ob this is a WIP .. i will start in on the rest of your feedback tomorrow probably .. my daughter is coming for dinner
JB
Jordan Brown
Fri, Aug 8, 2025 3:47 PM

On 8/8/2025 8:19 AM, vulcan_--- via Discuss wrote:

i am using this existing page
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Mathematical_Functions

to reorg the built-in functions, eventually to be replaced by a new
section Built-In Functions
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_of_Builtin_Functions

There are two obvious ways to structure a description of functions - by
the fact that they are functions and the types that they accept and
return, and by what general features they implement and support.

I would lean toward the latter.  Nobody is going to come along saying "I
need to find a function".  Rather, they are going to say "I want to do
X; what are my tools?".

That would suggest that sin(), log(), and abs() are mathematical
(perhaps with a subcategory for trigonometry), and that chr() and ord()
are both string functions.  I would put textmetrics() and fontmetrics()
on the Text page, with text(), since they are used together.

len() is a bit tricky as it's both a string function and a list function
(and an object function).  I'd probably flip a coin, put it with either
strings or lists, and put a cross-reference from the other.

There should be a master list, probably alphabetical, somewhere, but
that should only be an index.

Working primarily from names, or primarily from types, reminds me of a
comment I once heard about UNIX manual pages:  UNIX manual pages are a
bunch of questions and answers, indexed by answer.

On 8/8/2025 8:19 AM, vulcan_--- via Discuss wrote: > > i am using this existing page > https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Mathematical_Functions > > to reorg the built-in functions, eventually to be replaced by a new > section Built-In Functions > <https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_of_Builtin_Functions> > There are two obvious ways to structure a description of functions - by the fact that they are functions and the types that they accept and return, and by what general features they implement and support. I would lean toward the latter.  Nobody is going to come along saying "I need to find a function".  Rather, they are going to say "I want to do X; what are my tools?". That would suggest that sin(), log(), and abs() are mathematical (perhaps with a subcategory for trigonometry), and that chr() and ord() are both string functions.  I would put textmetrics() and fontmetrics() on the Text page, with text(), since they are used together. len() is a bit tricky as it's both a string function and a list function (and an object function).  I'd probably flip a coin, put it with either strings or lists, and put a cross-reference from the other. There should be a master list, probably alphabetical, somewhere, but that should only be an index. Working primarily from names, or primarily from types, reminds me of a comment I once heard about UNIX manual pages:  UNIX manual pages are a bunch of questions and answers, indexed by answer.
V
vulcan_@mac.com
Sat, Aug 9, 2025 12:07 AM

Jordan Brown wrote:

comment I once heard about UNIX manual pages:  UNIX manual pages are a
bunch of questions and answers, indexed by answer.

that is a very useful guidance

thx

Jordan Brown wrote: > > comment I once heard about UNIX manual pages:  UNIX manual pages are a > bunch of questions and answers, indexed by answer. that is a very useful guidance thx