Special variable scoping rules

classic Classic list List threaded Threaded
28 messages Options
12
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Special variable scoping rules

adrian
I hit upon an interesting use case.  If I have two files A and B, and B includes A (keyword include) which contains a function/module that references a special variable, setting the variable in B will be seen in A.  However, if B uses A (keyword use), then the function/module will not see the special variable set in the global scope of B (though, it can be passed as a parameter).

This doesn't seem consistent.  What do you think?
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

kintel
Administrator
> On Feb 3, 2017, at 14:02, adrian <[hidden email]> wrote:
>
> This doesn't seem consistent.  What do you think?
>

To avoid any misunderstandings, could your provide testable code for this, including expected and actual results?

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

adrian
Sure:
// File A

$special="No, I'm just weird.";
function special() = $special;
module special()
{
    echo($special);
}

// File B

include<A.scad>
//use<A.scad>
echo (special());
$special = "I'm special!";
special();
If include is used, then "I'm special!" comes up twice, this is what I expected for both include and use. When use is used, then "No, I'm just weird." comes up.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

nophead
When file A.scad is included there is only one top level scope so it works as expected. I.e. just the same as if you pasted A.scad into B.scad.

When you use a file things get very weird. The used file has its own separate top level scope. But one would expect dynamic scope variables to override that when functions and modules are called.

This is the work around that I use and I don't have an explanation why it works.

// File A

special= $special == undef ? "No, I'm just weird." : $special;
function special() = special;
module special()
{
    echo(special, $special);
}

If $special is not set in B.scad then it prints:
ECHO: "No, I'm just weird."

ECHO: "No, I'm just weird.", undef


If $special is set then it prints:

ECHO: "I'm special!"

ECHO: "I'm special!", "I'm special!"





On 3 February 2017 at 22:06, adrian <[hidden email]> wrote:
Sure:
// File A

$special="No, I'm just weird.";
function special() = $special;
module special()
{
    echo($special);
}

// File B

include<A.scad>
//use<A.scad>
echo (special());
$special = "I'm special!";
special();
If include is used, then "I'm special!" comes up twice, this is what I expected for both include and use. When use is used, then "No, I'm just weird." comes up.

View this message in context: Re: Special variable scoping rules

Sent from the OpenSCAD mailing list archive at Nabble.com.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

Ronaldo
nophead wrote
This is the work around that I use and I don't have an explanation why it
works.
It seems that when you use an OpenSCAD file, the assignments in the used file override the assignments in the main file.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

adrian
So to me, this sounds like it is a bug.  It's definitely not what I would have expected.  What does eveyone else think?
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

nophead
It's hard to say because OpenSCAD's scope rules are unique, so you can't compare them with an existing language to say what is expected in this situation. You can't normally inject symbols into another module you are using.

It seems you can inject special variables but whether they should override special variables in the used module I am not sure. It is probably more useful if they do but you can work around it.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

kintel
Administrator
In reply to this post by adrian
Thanks Adrian,

Special variables is probably one of the worst design mistakes in OpenSCAD. I would strongly recommend not using your own special variables. The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’.

I’ll look into this specific case as soon as I find enough space cycles..

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

nophead
They are essential for passing parameters to children. For example:

function layout_offset(widths, i, gap = 2) = i == 0 ? widths[0] / 2
                                                                             : layout_offset(widths, i - 1, gap) + widths[i - 1] / 2 + gap + widths[i] / 2;

module layout(widths, gap = 2) {
    for($i = [0 : len(widths) - 1])
        translate([layout_offset(widths, $i, gap), 0, 0])
            children();
}

layout([for(p = pillars) pillar_od(p)])
     pillar(pillars[$i]);



On 8 February 2017 at 05:10, Marius Kintel <[hidden email]> wrote:
Thanks Adrian,

Special variables is probably one of the worst design mistakes in OpenSCAD. I would strongly recommend not using your own special variables. The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’.

I’ll look into this specific case as soon as I find enough space cycles..

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

kintel
Administrator
On Feb 8, 2017, at 03:47, nop head <[hidden email]> wrote:
>
> They are essential for passing parameters to children. For example:
>
Yes, there are naturally good uses of such constructs (for the above-average power users :))
A good discussion would be the best way of achieving this in the context of a future language revamp.

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

adrian
In reply to this post by nophead
Nophead,

That example doesn't look "essential". Seems that could be achieved just by using variable i, and passing i as a parameter.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

adrian
In reply to this post by nophead
Oh wait, I misunderstood. That's clever.
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

doug.moen
In reply to this post by kintel
Marius: "Special variables is probably one of the worst design mistakes in OpenSCAD."
Nop Head: "They are essential for passing parameters to children." <code example>
Adrian: "That's clever." [My response: but not so easy to understand.]
Marius: "A good discussion would be the best way of achieving this in the context of a future language revamp."

I'm continuing to work on my new geometry language ("a superset of a subset of OpenSCAD2").

I haven't implemented special variables, because they are complicated and tricky, the correct semantics are not clear to me, and I haven't needed them so far. I was originally planning to implement them, but now I don't see the need. What I do have (that's missing from OpenSCAD) are records {n=1, s="abc"}, similar to Javascript object literals, and function literals (x,y)->x+y.

This language is *not* OpenSCAD, the programming style is different.

Instead of implicitly passing special variables, I can explicitly pass a record value containing name/value pairs. The same programming style is used in Javascript. The explicit style makes it easier to see what is going on, and is more expressive.

OpenSCAD modules pass children "by name", evaluating the children argument expression each time children() is referenced. In my language, shapes are passed by value, all function arguments are evaluated before the function is entered. To get the effect that Nop Head demonstrated, I pass a function value, but that is made very convenient using function literals.

OpenSCAD:
module layout(widths, gap = 2) {
    for($i = [0 : len(widths) - 1])
        translate([layout_offset(widths, $i, gap), 0, 0])
            children();
}
layout([for(p = pillars) pillar_od(p)])
     pillar(pillars[$i]);

My language:
layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

Marius: "The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’."

OpenSCAD tessellates curved surfaces as early as possible. My language uses functional representation and tessellates curved surfaces as *late* as possible. Spheres, cylinders, B-splines (possible future extension), and their unions and intersections, are all represented exactly, and are not tessellated until you export to STL. As a result, the motivation to use dynamic binding for tessellation density variables (you want to avoid passing these variables explicitly through multiple layers of function calls) doesn't exist in my language.

I don't use circle($fn=6) to construct a hexagon, I use regular_polygon(6). Similarly, instead of cylinder($fn=6) I use prism(6).

On 8 February 2017 at 00:10, Marius Kintel <[hidden email]> wrote:
Thanks Adrian,

Special variables is probably one of the worst design mistakes in OpenSCAD. I would strongly recommend not using your own special variables. The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’.

I’ll look into this specific case as soon as I find enough space cycles..

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

nophead
Very nice, no doubt, but the beauty of OpenSCAD was I could immediately read and understand it because of its familiar syntax. It took me virtually no learning time at all to be able to code Mendel90.

I can't understand you example code because it looks like no language I have used. Why square brackets in union()?

Why i->pillar(pillars[i]); Looks like a pointer dereference but i is an index. Perhaps lambda synstax but I don't see why i is even in scope there.

I doubt I will take the time to learn a more complex language and unfamiliar language when OpenSCAD does everything I need and is very simple to read and write.

On 8 February 2017 at 20:37, doug moen <[hidden email]> wrote:
Marius: "Special variables is probably one of the worst design mistakes in OpenSCAD."
Nop Head: "They are essential for passing parameters to children." <code example>
Adrian: "That's clever." [My response: but not so easy to understand.]
Marius: "A good discussion would be the best way of achieving this in the context of a future language revamp."

I'm continuing to work on my new geometry language ("a superset of a subset of OpenSCAD2").

I haven't implemented special variables, because they are complicated and tricky, the correct semantics are not clear to me, and I haven't needed them so far. I was originally planning to implement them, but now I don't see the need. What I do have (that's missing from OpenSCAD) are records {n=1, s="abc"}, similar to Javascript object literals, and function literals (x,y)->x+y.

This language is *not* OpenSCAD, the programming style is different.

Instead of implicitly passing special variables, I can explicitly pass a record value containing name/value pairs. The same programming style is used in Javascript. The explicit style makes it easier to see what is going on, and is more expressive.

OpenSCAD modules pass children "by name", evaluating the children argument expression each time children() is referenced. In my language, shapes are passed by value, all function arguments are evaluated before the function is entered. To get the effect that Nop Head demonstrated, I pass a function value, but that is made very convenient using function literals.

OpenSCAD:
module layout(widths, gap = 2) {
    for($i = [0 : len(widths) - 1])
        translate([layout_offset(widths, $i, gap), 0, 0])
            children();
}
layout([for(p = pillars) pillar_od(p)])
     pillar(pillars[$i]);

My language:
layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

Marius: "The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’."

OpenSCAD tessellates curved surfaces as early as possible. My language uses functional representation and tessellates curved surfaces as *late* as possible. Spheres, cylinders, B-splines (possible future extension), and their unions and intersections, are all represented exactly, and are not tessellated until you export to STL. As a result, the motivation to use dynamic binding for tessellation density variables (you want to avoid passing these variables explicitly through multiple layers of function calls) doesn't exist in my language.

I don't use circle($fn=6) to construct a hexagon, I use regular_polygon(6). Similarly, instead of cylinder($fn=6) I use prism(6).

On 8 February 2017 at 00:10, Marius Kintel <[hidden email]> wrote:
Thanks Adrian,

Special variables is probably one of the worst design mistakes in OpenSCAD. I would strongly recommend not using your own special variables. The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’.

I’ll look into this specific case as soon as I find enough space cycles..

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

doug.moen
Hi Nop Head. The language I'm working on is for me, not for you. I can build geometric objects in it that I can't construct in OpenSCAD, but your uses are a lot different than mine, so I wouldn't expect it to be useful to you. And yes, it isn't a C-like language.

My implementation of union is a function that takes a list of shapes as an argument. That was the simplest code I could write that worked. I have to invoke union explicitly here because I haven't implemented "implicit union" yet, except at the top level.

i->pillar(pillars[i]) is a function literal with a single parameter 'i'. In Javascript, you would instead write i=>pillar(pillars[i]). C++11 uses -> in its syntax for lambda expressions, and so do many other languages.

The reason I responded is because Marius surprised me by inviting a discussion about how to get rid of special variables in a future revamp of OpenSCAD. Since I'm creating a related geometry language that already works that way, my experiences would be relevant to that discussion.

On 8 February 2017 at 16:01, nop head <[hidden email]> wrote:
Very nice, no doubt, but the beauty of OpenSCAD was I could immediately read and understand it because of its familiar syntax. It took me virtually no learning time at all to be able to code Mendel90.

I can't understand you example code because it looks like no language I have used. Why square brackets in union()?

Why i->pillar(pillars[i]); Looks like a pointer dereference but i is an index. Perhaps lambda synstax but I don't see why i is even in scope there.

I doubt I will take the time to learn a more complex language and unfamiliar language when OpenSCAD does everything I need and is very simple to read and write.

On 8 February 2017 at 20:37, doug moen <[hidden email]> wrote:
Marius: "Special variables is probably one of the worst design mistakes in OpenSCAD."
Nop Head: "They are essential for passing parameters to children." <code example>
Adrian: "That's clever." [My response: but not so easy to understand.]
Marius: "A good discussion would be the best way of achieving this in the context of a future language revamp."

I'm continuing to work on my new geometry language ("a superset of a subset of OpenSCAD2").

I haven't implemented special variables, because they are complicated and tricky, the correct semantics are not clear to me, and I haven't needed them so far. I was originally planning to implement them, but now I don't see the need. What I do have (that's missing from OpenSCAD) are records {n=1, s="abc"}, similar to Javascript object literals, and function literals (x,y)->x+y.

This language is *not* OpenSCAD, the programming style is different.

Instead of implicitly passing special variables, I can explicitly pass a record value containing name/value pairs. The same programming style is used in Javascript. The explicit style makes it easier to see what is going on, and is more expressive.

OpenSCAD modules pass children "by name", evaluating the children argument expression each time children() is referenced. In my language, shapes are passed by value, all function arguments are evaluated before the function is entered. To get the effect that Nop Head demonstrated, I pass a function value, but that is made very convenient using function literals.

OpenSCAD:
module layout(widths, gap = 2) {
    for($i = [0 : len(widths) - 1])
        translate([layout_offset(widths, $i, gap), 0, 0])
            children();
}
layout([for(p = pillars) pillar_od(p)])
     pillar(pillars[$i]);

My language:
layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

Marius: "The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’."

OpenSCAD tessellates curved surfaces as early as possible. My language uses functional representation and tessellates curved surfaces as *late* as possible. Spheres, cylinders, B-splines (possible future extension), and their unions and intersections, are all represented exactly, and are not tessellated until you export to STL. As a result, the motivation to use dynamic binding for tessellation density variables (you want to avoid passing these variables explicitly through multiple layers of function calls) doesn't exist in my language.

I don't use circle($fn=6) to construct a hexagon, I use regular_polygon(6). Similarly, instead of cylinder($fn=6) I use prism(6).

On 8 February 2017 at 00:10, Marius Kintel <[hidden email]> wrote:
Thanks Adrian,

Special variables is probably one of the worst design mistakes in OpenSCAD. I would strongly recommend not using your own special variables. The main purpose of introducing these was to allow dynamic binding of tessellation density ($fn/$fs/$fa) variables, but the mechanism was (somehow) extended to catch all variables starting with ‘$’.

I’ll look into this specific case as soon as I find enough space cycles..

 -Marius


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org


_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

adrian
doug.moen wrote
i->pillar(pillars[i]) is a function literal with a single parameter 'i'. In Javascript, you would instead write i=>pillar(pillars[i]). C++11 uses -> in its syntax for lambda expressions, and so do many other languages.

Sorry, I am having difficulty trying to understand you syntax as well. Given:

layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

I take it that layout(widths, gap=2) is a module definition.

 f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);

would appear to be the body of that definition.

layout([for(p = pillars) pillar_od(p)]) would appear to be calling the module with [for(p = pillars) pillar_od(p)] being passed to widths, and i->pillar(pillars[i]); would then appear to be the children you will be passing?

Within the body of your module, you are referencing f, which is being assigned within the body of the module, but not assigned quite yet.

I'm really having a lot of difficulty as to what you are trying to express here.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

nophead
It would be more normal to have f in the argument list and pass the function literal inside the parenthesis.
layout(widths, f, gap=2 = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)], i->pillar(pillars[i]));

But if you have function parameters then you can pass the width function instead of list of widths.


On 9 February 2017 at 00:33, adrian <[hidden email]> wrote:
doug.moen wrote
i->pillar(pillars[i]) is a function literal with a single parameter 'i'. In Javascript, you would instead write i=>pillar(pillars[i]). C++11 uses -> in its syntax for lambda expressions, and so do many other languages.

Sorry, I am having difficulty trying to understand you syntax as well. Given:

layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

I take it that layout(widths, gap=2) is a module definition.

 f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);

would appear to be the body of that definition.

layout([for(p = pillars) pillar_od(p)]) would appear to be calling the module with [for(p = pillars) pillar_od(p)] being passed to widths, and i->pillar(pillars[i]); would then appear to be the children you will be passing?

Within the body of your module, you are referencing f, which is being assigned within the body of the module, but not assigned quite yet.

I'm really having a lot of difficulty as to what you are trying to express here.



View this message in context: Re: Special variable scoping rules
Sent from the OpenSCAD mailing list archive at Nabble.com.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

doug.moen
In reply to this post by adrian
In OpenSCAD, you can write
   translate([10,10]) square(5)

I wanted to support that syntax in my language. I don't have OpenSCAD-style modules, only functions. So translate and square are functions that return shapes.

Obviously it would be possible to use
    translate([10,10], square(5))
but I like the OpenSCAD syntax better.

So I defined translate(vector) as a function that returns a 2nd function that takes a shape as an argument, and returns the translated shape as a result. So you can write
    translate([10,10])(square(5))
Then I added another grammar rule that lets you omit the parentheses around an argument list if there is a single argument, that is a function call beginning with an identifier. It's an abbreviation. So you can abbreviate the above expression as:
    translate([10,10]) square(5)

I guess it is an open question whether the OpenSCAD "module call" syntax is better than the standard function call syntax. But this is my experimental toy language, so I decided to implement this syntax to see if it is usable. I'm not 100% sure, but it seems okay so far.

The actual definition of the `translate` function, in the standard library, has this form:
    translate(vector) shape = ... ;
Here, 'vector' and 'shape' are both formal parameters. I don't have modules, so I don't have 'children()'. The 'shape' parameter is used in place of 'children()'. You can also write the same function definition like this:
    translate(vector)(shape) = ... ;
but the same grammar production that lets me leave off the final set of parentheses in the call to translate also lets me omit the parentheses around 'shape' in the definition, so I got used to writing function definitions this way.

PS you can also write
    v = [10,10];
    s = square(5);
    translate(v) s;
So the definition of translate consists of a prototypical call to the function, followed by '=', followed by the body.

Doug.

On 8 February 2017 at 19:33, adrian <[hidden email]> wrote:
doug.moen wrote
i->pillar(pillars[i]) is a function literal with a single parameter 'i'. In Javascript, you would instead write i=>pillar(pillars[i]). C++11 uses -> in its syntax for lambda expressions, and so do many other languages.

Sorry, I am having difficulty trying to understand you syntax as well. Given:

layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

I take it that layout(widths, gap=2) is a module definition.

 f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);

would appear to be the body of that definition.

layout([for(p = pillars) pillar_od(p)]) would appear to be calling the module with [for(p = pillars) pillar_od(p)] being passed to widths, and i->pillar(pillars[i]); would then appear to be the children you will be passing?

Within the body of your module, you are referencing f, which is being assigned within the body of the module, but not assigned quite yet.

I'm really having a lot of difficulty as to what you are trying to express here.



View this message in context: Re: Special variable scoping rules
Sent from the OpenSCAD mailing list archive at Nabble.com.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

adrian
doug.moen wrote
i->pillar(pillars[i]) is a function literal with a single parameter 'i'. In Javascript, you would instead write i=>pillar(pillars[i]). C++11 uses -> in its syntax for lambda expressions, and so do many other languages.

Oh, and C++11 doesn't use -> for its lambda expressions exactly, it is an artifact of how regular functions signatures can now be written as:

auto fn() -> ret_type

Allowing for return type inference. Lambda only uses -> because it allows for the return type to be explicitly specified instead of inferred.

I'm understanding your code a bit more, but I'm still trying to wrap my head around it. In

layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

What is the i to the left of the -> in i->pillar(pillars[i]); supposed to be? Is it getting the i from the f(i)? In which case, it would appear that you are doing something similar to what nophead is doing. If not, then I'm just lost.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Special variable scoping rules

doug.moen
    i->pillar(pillars[i])
This is a function literal. The formal parameter is 'i', the body of the function is 'pillar(pillars[i])'.
Just as if you had defined
    F(i) = pillar(pillars[i]);
and then used the value of 'F'.

The layout function has a formal parameter 'f', and when layout is called, the function "i->pillar(pillars[i])" is bound to the parameter 'f'.

It might have been clearer if I had used a different name than 'i' for the formal parameter, to make it clear that this is a different 'i' than the 'i' used in the for loop. I will also get rid of the "curried" function call syntax, which is causing confusion.
layout(widths, gap, f) = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout(
    [for(p = pillars) pillar_od(p)], 
    2,
    x->pillar(pillars[x]));

In the functional programming style that I am using, it is common to pass functions as arguments and return functions as results. It helps to have a very terse syntax for anonymous function literals.

The technique that Nop Head is using is an advanced technique that isn't documented in the OpenSCAD manual, and probably most people don't know the feature exists. To do the same thing in my language, you must instead pass functions as arguments, which is arguably also an advanced technique.

Another way to write the layout function in my language is to pass a list of shapes as the third argument. This is less "magical" than passing a function argument, I think.
layout(widths, gap, shapes) = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            shapes[i]
]);
layout(
    [for (p = pillars) pillar_od(p)], 
    2,
    [for (p = pillars) pillar(p)]);


On 8 February 2017 at 20:58, adrian <[hidden email]> wrote:
doug.moen wrote
i->pillar(pillars[i]) is a function literal with a single parameter 'i'. In Javascript, you would instead write i=>pillar(pillars[i]). C++11 uses -> in its syntax for lambda expressions, and so do many other languages.

Oh, and C++11 doesn't use -> for its lambda expressions exactly, it is an artifact of how regular functions signatures can now be written as:

auto fn() -> ret_type

Allowing for return type inference. Lambda only uses -> because it allows for the return type to be explicitly specified instead of inferred.

I'm understanding your code a bit more, but I'm still trying to wrap my head around it. In

layout(widths, gap=2) f = union([
    for (i=[0..len(widths) - 1])
        translate([layout_offset(widths, i, gap), 0, 0])
            f(i)
]);
layout([for(p = pillars) pillar_od(p)])
    i->pillar(pillars[i]);

What is the i to the left of the -> in i->pillar(pillars[i]); supposed to be? Is it getting the i from the f(i)? In which case, it would appear that you are doing something similar to what nophead is doing. If not, then I'm just lost.



View this message in context: Re: Special variable scoping rules
Sent from the OpenSCAD mailing list archive at Nabble.com.

_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org



_______________________________________________
OpenSCAD mailing list
[hidden email]
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
12
Loading...