discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Re: Making modules first class without breaking existing Openscad code

JB
Jordan Brown
Fri, Sep 2, 2022 12:32 AM

OK, I think I'm starting to wrap my mind around what you're thinking of,
and if I'm right then it's neither what I would call a module reference
nor exactly geometry.

When you say:

x = module cube([1,2,3]);

I have two mental models of what that might mean:

  • You are defining an unnamed module, that doesn't take any arguments,
    and assigning a reference to it to x.  That unnamed module creates a
    cube with dimensions [1,2,3].  (That module has not yet been
    evaluated, so there is no geometry to add to the model.)
  • You are creating cube with dimensions [1,2,3], and putting some
    geometric representation of that cube into x.  (You are not adding
    to the model, at least not yet.)

Neither of those quite align with what you seem to want to do.

If it's an unnamed module, in non-trivial cases you can't say anything
interesting about "members", because they might have values that can't
be known until the module is evaluated.

If it's a representation of the object created, you also can't say
much about the "members", because all of the interesting values may have
been lost in the evaluation.

I think that maybe what you're thinking is that you are going to
capture the name of the (one) module being invoked, and you're going to
evaluate its arguments and capture them.  You could then access the
argument values, and you could invoke the module with those arguments
(and no others).

Let's look at a more complex example.  Here's a module that creates a
wide-screen television based on its diagonal and its thickness.

module television(diagonal, thickness) {
    // Ratio for a wide-screen TV
    nominal_h = 9;
    nominal_w = 16;
    nominal_diagonal = sqrt(nominal_h^2 + nominal_w^2);
    // Multiplier for *this* TV
    ratio = diagonal/nominal_diagonal;
    // Resulting height and width
    h = nominal_h * ratio;
    w = nominal_w * ratio;
    cube([w, thickness, h]);
    // add some feet
    translate([0,0,-1]) cube([1,thickness,1]);
    translate([w-1,0,-1]) cube([1,thickness,1]);
    echo(h=h, w=w, diagonal=diagonal);
}

Now, how would this fit into your scheme?

x = module television(55, 2);

What can you say about x?  What are the "members" of x?  When would the
echo() happen?

If I'm understanding correctly, you're suggesting that x.diagonal=55 and
x.thickness=2, and the echo() has not yet happened, and will happen when
and if x() happens.

And I think x() always creates a 55-unit-diagonal television, 2 units thick.

Is that what you intend?


Over the last few years, a number of people have been working on ideas
related to this.

You can find some of the thinking at

https://github.com/openscad/openscad/pull/3077 (function
literals/references)
https://github.com/openscad/openscad/pull/3087 ("object literals")
https://github.com/openscad/openscad/issues/3088 (various related topics)
https://github.com/openscad/openscad/pull/3956 (rendering geometry into
data)

I tried to pull some of those concepts together into a coherent whole at
https://docs.google.com/document/d/14siqbr9CyGuA2tY6dvTqiF3Hf9zFMyefk7YdaUJSn3Q

That doc focuses on geometry-as-data, but has a brief discussion of
module references (sometimes maybe called module literals) toward the
bottom - mostly to point out that they are an orthogonal concept.

I've only just started to push that model, and so I can't claim that I
have buy-in, but based on some conversations today I think maybe there's
interest.

I don't think anything in there is precisely aligned with what you're
thinking of, but I think it's close and might be close enough.

OK, I think I'm starting to wrap my mind around what you're thinking of, and if I'm right then it's neither what I would call a module reference nor exactly geometry. When you say: x = module cube([1,2,3]); I have two mental models of what that might mean: * You are defining an unnamed module, that doesn't take any arguments, and assigning a reference to it to x.  That unnamed module creates a cube with dimensions [1,2,3].  (That module has not yet been evaluated, so there is no geometry to add to the model.) * You are creating cube with dimensions [1,2,3], and putting some geometric representation of that cube into x.  (You are not adding to the model, at least not yet.) Neither of those quite align with what you seem to want to do. If it's an unnamed module, in non-trivial cases you can't say anything interesting about "members", because they might have values that can't be known until the module is evaluated. If it's a representation of the object created, you *also* can't say much about the "members", because all of the interesting values may have been lost in the evaluation. I think that *maybe* what you're thinking is that you are going to capture the name of the (one) module being invoked, and you're going to evaluate its arguments and capture them.  You could then access the argument values, and you could invoke the module with those arguments (and no others). Let's look at a more complex example.  Here's a module that creates a wide-screen television based on its diagonal and its thickness. module television(diagonal, thickness) { // Ratio for a wide-screen TV nominal_h = 9; nominal_w = 16; nominal_diagonal = sqrt(nominal_h^2 + nominal_w^2); // Multiplier for *this* TV ratio = diagonal/nominal_diagonal; // Resulting height and width h = nominal_h * ratio; w = nominal_w * ratio; cube([w, thickness, h]); // add some feet translate([0,0,-1]) cube([1,thickness,1]); translate([w-1,0,-1]) cube([1,thickness,1]); echo(h=h, w=w, diagonal=diagonal); } Now, how would this fit into your scheme? x = module television(55, 2); What can you say about x?  What are the "members" of x?  When would the echo() happen? If I'm understanding correctly, you're suggesting that x.diagonal=55 and x.thickness=2, and the echo() has not yet happened, and will happen when and if x() happens. And I think x() always creates a 55-unit-diagonal television, 2 units thick. Is that what you intend? --- Over the last few years, a number of people have been working on ideas related to this. You can find some of the thinking at https://github.com/openscad/openscad/pull/3077 (function literals/references) https://github.com/openscad/openscad/pull/3087 ("object literals") https://github.com/openscad/openscad/issues/3088 (various related topics) https://github.com/openscad/openscad/pull/3956 (rendering geometry into data) I tried to pull some of those concepts together into a coherent whole at https://docs.google.com/document/d/14siqbr9CyGuA2tY6dvTqiF3Hf9zFMyefk7YdaUJSn3Q That doc focuses on geometry-as-data, but has a brief discussion of module references (sometimes maybe called module literals) toward the bottom - mostly to point out that they are an orthogonal concept. I've only just started to push that model, and so I can't claim that I have buy-in, but based on some conversations today I think maybe there's interest. I don't think anything in there is precisely aligned with what you're thinking of, but I think it's close and might be close enough.
MM
Michael Marx
Fri, Sep 2, 2022 1:26 AM

Just in case it is not clear, Andy Little is kwikius of the GitHub https://github.com/openscad/openscad/issues/4336  Issue.


From: Jordan Brown [mailto:openscad@jordan.maileater.net]
Sent: Fri, 2 Sep 2022 10:33
To: Andy Little via Discuss
Cc: kwikius@yahoo.com
Subject: [OpenSCAD] Re: Making modules first class without breaking existing Openscad code

OK, I think I'm starting to wrap my mind around what you're thinking of, and if I'm right then it's neither what I would call a module reference nor exactly geometry.

When you say:

x = module cube([1,2,3]);

I have two mental models of what that might mean:

  • You are defining an unnamed module, that doesn't take any arguments, and assigning a reference to it to x.  That unnamed module creates a cube with dimensions [1,2,3].  (That module has not yet been evaluated, so there is no geometry to add to the model.)
  • You are creating cube with dimensions [1,2,3], and putting some geometric representation of that cube into x.  (You are not adding to the model, at least not yet.)

Neither of those quite align with what you seem to want to do.

If it's an unnamed module, in non-trivial cases you can't say anything interesting about "members", because they might have values that can't be known until the module is evaluated.

If it's a representation of the object created, you also can't say much about the "members", because all of the interesting values may have been lost in the evaluation.

I think that maybe what you're thinking is that you are going to capture the name of the (one) module being invoked, and you're going to evaluate its arguments and capture them.  You could then access the argument values, and you could invoke the module with those arguments (and no others).

Let's look at a more complex example.  Here's a module that creates a wide-screen television based on its diagonal and its thickness.

module television(diagonal, thickness) {
// Ratio for a wide-screen TV
nominal_h = 9;
nominal_w = 16;
nominal_diagonal = sqrt(nominal_h^2 + nominal_w^2);
// Multiplier for this TV
ratio = diagonal/nominal_diagonal;
// Resulting height and width
h = nominal_h * ratio;
w = nominal_w * ratio;
cube([w, thickness, h]);
// add some feet
translate([0,0,-1]) cube([1,thickness,1]);
translate([w-1,0,-1]) cube([1,thickness,1]);
echo(h=h, w=w, diagonal=diagonal);
}

Now, how would this fit into your scheme?

x = module television(55, 2);

What can you say about x?  What are the "members" of x?  When would the echo() happen?

If I'm understanding correctly, you're suggesting that x.diagonal=55 and x.thickness=2, and the echo() has not yet happened, and will happen when and if x() happens.

And I think x() always creates a 55-unit-diagonal television, 2 units thick.

Is that what you intend?


Over the last few years, a number of people have been working on ideas related to this.

You can find some of the thinking at

https://github.com/openscad/openscad/pull/3077 (function literals/references)
https://github.com/openscad/openscad/pull/3087 ("object literals")
https://github.com/openscad/openscad/issues/3088 (various related topics)
https://github.com/openscad/openscad/pull/3956 (rendering geometry into data)

I tried to pull some of those concepts together into a coherent whole at
https://docs.google.com/document/d/14siqbr9CyGuA2tY6dvTqiF3Hf9zFMyefk7YdaUJSn3Q

That doc focuses on geometry-as-data, but has a brief discussion of module references (sometimes maybe called module literals) toward the bottom - mostly to point out that they are an orthogonal concept.

I've only just started to push that model, and so I can't claim that I have buy-in, but based on some conversations today I think maybe there's interest.

I don't think anything in there is precisely aligned with what you're thinking of, but I think it's close and might be close enough.

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

Just in case it is not clear, Andy Little is kwikius of the GitHub <https://github.com/openscad/openscad/issues/4336> Issue. _____ From: Jordan Brown [mailto:openscad@jordan.maileater.net] Sent: Fri, 2 Sep 2022 10:33 To: Andy Little via Discuss Cc: kwikius@yahoo.com Subject: [OpenSCAD] Re: Making modules first class without breaking existing Openscad code OK, I think I'm starting to wrap my mind around what you're thinking of, and if I'm right then it's neither what I would call a module reference nor exactly geometry. When you say: x = module cube([1,2,3]); I have two mental models of what that might mean: * You are defining an unnamed module, that doesn't take any arguments, and assigning a reference to it to x. That unnamed module creates a cube with dimensions [1,2,3]. (That module has not yet been evaluated, so there is no geometry to add to the model.) * You are creating cube with dimensions [1,2,3], and putting some geometric representation of that cube into x. (You are not adding to the model, at least not yet.) Neither of those quite align with what you seem to want to do. If it's an unnamed module, in non-trivial cases you can't say anything interesting about "members", because they might have values that can't be known until the module is evaluated. If it's a representation of the object created, you *also* can't say much about the "members", because all of the interesting values may have been lost in the evaluation. I think that *maybe* what you're thinking is that you are going to capture the name of the (one) module being invoked, and you're going to evaluate its arguments and capture them. You could then access the argument values, and you could invoke the module with those arguments (and no others). Let's look at a more complex example. Here's a module that creates a wide-screen television based on its diagonal and its thickness. module television(diagonal, thickness) { // Ratio for a wide-screen TV nominal_h = 9; nominal_w = 16; nominal_diagonal = sqrt(nominal_h^2 + nominal_w^2); // Multiplier for *this* TV ratio = diagonal/nominal_diagonal; // Resulting height and width h = nominal_h * ratio; w = nominal_w * ratio; cube([w, thickness, h]); // add some feet translate([0,0,-1]) cube([1,thickness,1]); translate([w-1,0,-1]) cube([1,thickness,1]); echo(h=h, w=w, diagonal=diagonal); } Now, how would this fit into your scheme? x = module television(55, 2); What can you say about x? What are the "members" of x? When would the echo() happen? If I'm understanding correctly, you're suggesting that x.diagonal=55 and x.thickness=2, and the echo() has not yet happened, and will happen when and if x() happens. And I think x() always creates a 55-unit-diagonal television, 2 units thick. Is that what you intend? --- Over the last few years, a number of people have been working on ideas related to this. You can find some of the thinking at https://github.com/openscad/openscad/pull/3077 (function literals/references) https://github.com/openscad/openscad/pull/3087 ("object literals") https://github.com/openscad/openscad/issues/3088 (various related topics) https://github.com/openscad/openscad/pull/3956 (rendering geometry into data) I tried to pull some of those concepts together into a coherent whole at https://docs.google.com/document/d/14siqbr9CyGuA2tY6dvTqiF3Hf9zFMyefk7YdaUJSn3Q That doc focuses on geometry-as-data, but has a brief discussion of module references (sometimes maybe called module literals) toward the bottom - mostly to point out that they are an orthogonal concept. I've only just started to push that model, and so I can't claim that I have buy-in, but based on some conversations today I think maybe there's interest. I don't think anything in there is precisely aligned with what you're thinking of, but I think it's close and might be close enough. -- This email has been checked for viruses by AVG antivirus software. www.avg.com
K
kwikius@yahoo.com
Fri, Sep 2, 2022 6:22 AM

Not quite sure why my email address is appearing instead of my name. Probably going to get a ton of spam!  Anyway to disable that feature?

Not quite sure why my email address is appearing instead of my name. Probably going to get a ton of spam! Anyway to disable that feature?
K
kwikius@yahoo.com
Fri, Sep 2, 2022 6:33 AM

Let's look at a more complex example.  Here's a module that creates a

wide-screen television based on its diagonal and its thickness.

module television(diagonal, thickness) {

 // Ratio for a wide-screen TV

 nominal_h = 9;

 nominal_w = 16;

 nominal_diagonal = sqrt(nominal_h^2 + nominal_w^2);

 // Multiplier for \*this\* TV

 ratio = diagonal/nominal_diagonal;

 // Resulting height and width

 h = nominal_h \* ratio;

 w = nominal_w \* ratio;

 cube(\[w, thickness, h\]);

 // add some feet

 translate(\[0,0,-1\]) cube(\[1,thickness,1\]);

 translate(\[w-1,0,-1\]) cube(\[1,thickness,1\]);

 echo(h=h, w=w, diagonal=diagonal);

}

x = module television(55,2);

What are the "members" of x?

diagonal = x.diagonal; // (55)

thickness = x.thickness; // (2)

nominal_h = x.nominal_h; //(9)

nominal_w = x.nominal_w; // (16)

nominal_diagonal = x.nominal_diagonal (18.3576)

ratio = x.ratio; //(2.99604)

h = x.h;  //(26.9644)

w = x.w; //(47.9367)

When would the echo() happen?

x();

(since echo(...) is actually a single_module_instantiation. see https://github.com/openscad/openscad/blob/master/src/core/parser.y#L314

If I'm understanding correctly, you're suggesting that x.diagonal=55 and

x.thickness=2, and the echo() has not yet happened, and will happen when

and if x() happens.

Yes !

And I think x() always creates a 55-unit-diagonal television, 2 units thick.

Is that what you intend?

Yes !

Over the last few years, a number of people have been working on ideas

related to this.

You can find some of the thinking at

https://github.com/openscad/openscad/pull/3077 (function

literals/references)

Very nice and also working in current openscad. The module_alias feature is intended to do something similar for modules. Perhaps module_alias should be renamed to module_literal ?

The module alias is complimentary. You can use a module_alias in the body of an object_literal I think or seee no reason why not.

you could presumably use a module_alias in a script also

https://github.com/openscad/openscad/pull/3956 (rendering geometry into data)

fun and potentially very useful but doesn’t really interact with the module_alias since it transfoms a module_instantiation into pure data. A quick read of the implementation suggest it instantiates module(s) and then removes them from the  AST. That might be a little hairy!

Anyway I don't think it has an effect on the module_alias feature

I tried to pull some of those concepts together into a coherent whole at

https://docs.google.com/document/d/14siqbr9CyGuA2tY6dvTqiF3Hf9zFMyefk7YdaUJSn3Q

That doc focuses on geometry-as-data, but has a brief discussion of

module references (sometimes maybe called module literals) toward the

bottom - mostly to point out that they are an orthogonal concept.

I've only just started to push that model, and so I can't claim that I

have buy-in, but based on some conversations today I think maybe there's

interest.

I don't think anything in there is precisely aligned with what you're

thinking of, but I think it's close and might be close enough.

from the doc

m = module(parameters) {...};

m is a reference to that module, can be invoked like so:

m(arguments);

looks like an alternative syntax for defining modules? but how do you refer to already defined modules?

you could probably extend the module_alias syntax to parameters

m = module(height) cube([1,1,height]);

m(20); // --> cube([1,1,20]);

> > Let's look at a more complex example. Here's a module that creates a > > wide-screen television based on its diagonal and its thickness. > > module television(diagonal, thickness) { > > // Ratio for a wide-screen TV > > nominal_h = 9; > > nominal_w = 16; > > nominal_diagonal = sqrt(nominal_h^2 + nominal_w^2); > > // Multiplier for \*this\* TV > > ratio = diagonal/nominal_diagonal; > > // Resulting height and width > > h = nominal_h \* ratio; > > w = nominal_w \* ratio; > > cube(\[w, thickness, h\]); > > // add some feet > > translate(\[0,0,-1\]) cube(\[1,thickness,1\]); > > translate(\[w-1,0,-1\]) cube(\[1,thickness,1\]); > > echo(h=h, w=w, diagonal=diagonal); > > } > > x = module television(55,2); > > What are the "members" of x? diagonal = x.diagonal; // (55) thickness = x.thickness; // (2) nominal_h = x.nominal_h; //(9) nominal_w = x.nominal_w; // (16) nominal_diagonal = x.nominal_diagonal (18.3576) ratio = x.ratio; //(2.99604) h = x.h; //(26.9644) w = x.w; //(47.9367) > When would the echo() happen? x(); (since echo(...) is actually a single_module_instantiation. see https://github.com/openscad/openscad/blob/master/src/core/parser.y#L314 > If I'm understanding correctly, you're suggesting that x.diagonal=55 and > > x.thickness=2, and the echo() has not yet happened, and will happen when > > and if x() happens. Yes ! > And I think x() always creates a 55-unit-diagonal television, 2 units thick. > > Is that what you intend? Yes ! > Over the last few years, a number of people have been working on ideas > > related to this. > > You can find some of the thinking at > > https://github.com/openscad/openscad/pull/3077 (function > > literals/references) Very nice and also working in current openscad. The module_alias feature is intended to do something similar for modules. Perhaps module_alias should be renamed to module_literal ? > https://github.com/openscad/openscad/pull/3087 ("object literals") The module alias is complimentary. You can use a module_alias in the body of an object_literal I think or seee no reason why not. > https://github.com/openscad/openscad/issues/3088 (various related topics) you could presumably use a module_alias in a script also > https://github.com/openscad/openscad/pull/3956 (rendering geometry into data) fun and potentially very useful but doesn’t really interact with the module_alias since it transfoms a module_instantiation into pure data. A quick read of the implementation suggest it instantiates module(s) and then removes them from the AST. That might be a little hairy! > Anyway I don't think it has an effect on the module_alias feature > > I tried to pull some of those concepts together into a coherent whole at > > https://docs.google.com/document/d/14siqbr9CyGuA2tY6dvTqiF3Hf9zFMyefk7YdaUJSn3Q > > That doc focuses on geometry-as-data, but has a brief discussion of > > module references (sometimes maybe called module literals) toward the > > bottom - mostly to point out that they are an orthogonal concept. > > I've only just started to push that model, and so I can't claim that I > > have buy-in, but based on some conversations today I think maybe there's > > interest. > > I don't think anything in there is precisely aligned with what you're > > thinking of, but I think it's close and might be close enough. > > from the doc > > m = module(parameters) {...}; > > m is a reference to that module, can be invoked like so: > > m(arguments); looks like an alternative syntax for defining modules? but how do you refer to already defined modules? you could probably extend the module_alias syntax to parameters m = module(height) cube(\[1,1,height\]); m(20); // --> cube(\[1,1,20\]);