discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Functions literals / higher order functions

TP
Torsten Paul
Mon, Sep 16, 2019 1:08 AM

A couple of weeks ago there was a discussion about
function parameters for modules. I linked to the
proposal Doug made some time ago.

I wanted to have that feature for quite some time
so I had a closer look at this. The result is at
https://github.com/openscad/openscad/pull/3077
which is a prototype implementation of function
literals.

This still needs work, specifically verifying that
the parser changes did not break any existing
behavior and verification that the scoping works
as defined.

Anyone interested in helping to get that ready
for merging?

ciao,
Torsten.

A couple of weeks ago there was a discussion about function parameters for modules. I linked to the proposal Doug made some time ago. I wanted to have that feature for quite some time so I had a closer look at this. The result is at https://github.com/openscad/openscad/pull/3077 which is a prototype implementation of function literals. This still needs work, specifically verifying that the parser changes did not break any existing behavior and verification that the scoping works as defined. Anyone interested in helping to get that ready for merging? ciao, Torsten.
C
caterpillar
Mon, Sep 16, 2019 5:07 AM

It looks nice. This is helpful when developing flexible modules and
functions, such as sort, filter, etc.


http://openhome.cc

Sent from: http://forum.openscad.org/

It looks nice. This is helpful when developing flexible modules and functions, such as `sort`, `filter`, etc. ----- http://openhome.cc -- Sent from: http://forum.openscad.org/
TP
Torsten Paul
Wed, Sep 25, 2019 12:46 AM

So getting the variable scoping working needed a little
bit more work than anticipated, but it should be ok now.
Function literals capture the static scope as proposed
in:
https://github.com/doug-moen/openscad2/blob/master/rfc/Functions.md

Example with a function returning a function object
and using both lexical/static scoping of variable 'a'
and dynamic scoping of variable '$a'.


module m2(f1, f2) {
a = 3;
$a = 30;
echo(f1 = f1(0.03), f2 = f2(0.07));
}

module m1(f) {
a = 2;
$a = 20;
add2 = function(x) function(y) x + y + a + $a;
m2(f, add2(0.2));
}

a = 1;
$a = 10;
add1 = function(x) function(y) x + y + a + $a;
m1(add1(0.1));

// ECHO: f1 = 31.13, f2 = 32.27


Or for a more visual example defining a simple plot()
module taking a function.


module plot(count, f) {
for (x = [0 : count - 1]) {
translate([x, 0, 0]) cube([1, 1, 1 + f(x / count)]);
}
}

func_sin = function(x) 10 * sin(360 * x) + 10;
func_cos = function(x) 10 * cos(360 * x) + 10;
func_pow = function(x) 10 * pow(abs(4 * (x - 0.5)), 2);

funcs = [
[func_sin,  0, "red"],
[func_cos, 20, "green"],
[func_pow, 40, "yellow"]
];

for (func = funcs)
color(func[2])
translate([0, func[1], 0])
plot(100, func[0]);


Binary downloads of the current state of this branch:

Linux: https://app.circleci.com/jobs/github/openscad/openscad/3622/artifacts
Win32: https://app.circleci.com/jobs/github/openscad/openscad/3624/artifacts
Win64: https://app.circleci.com/jobs/github/openscad/openscad/3623/artifacts

ciao,
Torsten.

So getting the variable scoping working needed a little bit more work than anticipated, but it should be ok now. Function literals capture the static scope as proposed in: https://github.com/doug-moen/openscad2/blob/master/rfc/Functions.md Example with a function returning a function object and using both lexical/static scoping of variable 'a' and dynamic scoping of variable '$a'. --------------------------------------- module m2(f1, f2) { a = 3; $a = 30; echo(f1 = f1(0.03), f2 = f2(0.07)); } module m1(f) { a = 2; $a = 20; add2 = function(x) function(y) x + y + a + $a; m2(f, add2(0.2)); } a = 1; $a = 10; add1 = function(x) function(y) x + y + a + $a; m1(add1(0.1)); // ECHO: f1 = 31.13, f2 = 32.27 --------------------------------------- Or for a more visual example defining a simple plot() module taking a function. --------------------------------------- module plot(count, f) { for (x = [0 : count - 1]) { translate([x, 0, 0]) cube([1, 1, 1 + f(x / count)]); } } func_sin = function(x) 10 * sin(360 * x) + 10; func_cos = function(x) 10 * cos(360 * x) + 10; func_pow = function(x) 10 * pow(abs(4 * (x - 0.5)), 2); funcs = [ [func_sin, 0, "red"], [func_cos, 20, "green"], [func_pow, 40, "yellow"] ]; for (func = funcs) color(func[2]) translate([0, func[1], 0]) plot(100, func[0]); --------------------------------------- Binary downloads of the current state of this branch: Linux: https://app.circleci.com/jobs/github/openscad/openscad/3622/artifacts Win32: https://app.circleci.com/jobs/github/openscad/openscad/3624/artifacts Win64: https://app.circleci.com/jobs/github/openscad/openscad/3623/artifacts ciao, Torsten.
RD
Revar Desmera
Thu, Sep 26, 2019 9:45 PM

Very nice!  This will be helpful with several things.

-Revar

On Sep 24, 2019, at 5:47 PM, Torsten Paul Torsten.Paul@gmx.de wrote:

So getting the variable scoping working needed a little
bit more work than anticipated, but it should be ok now.
Function literals capture the static scope as proposed
in:
https://github.com/doug-moen/openscad2/blob/master/rfc/Functions.md

Example with a function returning a function object
and using both lexical/static scoping of variable 'a'
and dynamic scoping of variable '$a'.


module m2(f1, f2) {
a = 3;
$a = 30;
echo(f1 = f1(0.03), f2 = f2(0.07));
}

module m1(f) {
a = 2;
$a = 20;
add2 = function(x) function(y) x + y + a + $a;
m2(f, add2(0.2));
}

a = 1;
$a = 10;
add1 = function(x) function(y) x + y + a + $a;
m1(add1(0.1));

// ECHO: f1 = 31.13, f2 = 32.27


Or for a more visual example defining a simple plot()
module taking a function.


module plot(count, f) {
for (x = [0 : count - 1]) {
translate([x, 0, 0]) cube([1, 1, 1 + f(x / count)]);
}
}

func_sin = function(x) 10 * sin(360 * x) + 10;
func_cos = function(x) 10 * cos(360 * x) + 10;
func_pow = function(x) 10 * pow(abs(4 * (x - 0.5)), 2);

funcs = [
[func_sin,  0, "red"],
[func_cos, 20, "green"],
[func_pow, 40, "yellow"]
];

for (func = funcs)
color(func[2])
translate([0, func[1], 0])
plot(100, func[0]);


Binary downloads of the current state of this branch:

Linux: https://app.circleci.com/jobs/github/openscad/openscad/3622/artifacts
Win32: https://app.circleci.com/jobs/github/openscad/openscad/3624/artifacts
Win64: https://app.circleci.com/jobs/github/openscad/openscad/3623/artifacts

ciao,
Torsten.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

Very nice! This will be helpful with several things. -Revar > On Sep 24, 2019, at 5:47 PM, Torsten Paul <Torsten.Paul@gmx.de> wrote: > > So getting the variable scoping working needed a little > bit more work than anticipated, but it should be ok now. > Function literals capture the static scope as proposed > in: > https://github.com/doug-moen/openscad2/blob/master/rfc/Functions.md > > Example with a function returning a function object > and using both lexical/static scoping of variable 'a' > and dynamic scoping of variable '$a'. > > --------------------------------------- > > module m2(f1, f2) { > a = 3; > $a = 30; > echo(f1 = f1(0.03), f2 = f2(0.07)); > } > > module m1(f) { > a = 2; > $a = 20; > add2 = function(x) function(y) x + y + a + $a; > m2(f, add2(0.2)); > } > > a = 1; > $a = 10; > add1 = function(x) function(y) x + y + a + $a; > m1(add1(0.1)); > > // ECHO: f1 = 31.13, f2 = 32.27 > > --------------------------------------- > > Or for a more visual example defining a simple plot() > module taking a function. > > --------------------------------------- > > module plot(count, f) { > for (x = [0 : count - 1]) { > translate([x, 0, 0]) cube([1, 1, 1 + f(x / count)]); > } > } > > func_sin = function(x) 10 * sin(360 * x) + 10; > func_cos = function(x) 10 * cos(360 * x) + 10; > func_pow = function(x) 10 * pow(abs(4 * (x - 0.5)), 2); > > funcs = [ > [func_sin, 0, "red"], > [func_cos, 20, "green"], > [func_pow, 40, "yellow"] > ]; > > for (func = funcs) > color(func[2]) > translate([0, func[1], 0]) > plot(100, func[0]); > > --------------------------------------- > > Binary downloads of the current state of this branch: > > Linux: https://app.circleci.com/jobs/github/openscad/openscad/3622/artifacts > Win32: https://app.circleci.com/jobs/github/openscad/openscad/3624/artifacts > Win64: https://app.circleci.com/jobs/github/openscad/openscad/3623/artifacts > > ciao, > Torsten. > > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
TP
Torsten Paul
Mon, Oct 28, 2019 12:17 AM

Just to close this thread, function literals are
now available in snapshot versions > 2019-10-24.

The feature is marked experimental, so it needs
to be enabled Preferences -> Features.

ciao,
Torsten.

Just to close this thread, function literals are now available in snapshot versions > 2019-10-24. The feature is marked experimental, so it needs to be enabled Preferences -> Features. ciao, Torsten.
C
caterpillar
Mon, Oct 28, 2019 9:21 AM

Good news. It will give libraries great improvement. Looking forward to use
this feature in the next release.


https://openhome.cc

Sent from: http://forum.openscad.org/

Good news. It will give libraries great improvement. Looking forward to use this feature in the next release. ----- https://openhome.cc -- Sent from: http://forum.openscad.org/
A
adrianv
Sun, Nov 3, 2019 9:40 PM

I wanted to clarify something.  What if I want to write some code to operate
on functions, so

function foo(func) = ....;

If I want to pass an existing function like sin or cos, or maybe a function,
myfunc, that I defined, do I have to go through the whole syntax of

foo(function(x) sin(x))

or

foo(function(x,y) myfunc(x,y))

in order to do this?  Or if I want to define a function variable to equal
an existing function, similarly do I need to use the full syntax:

func_var = function(x) sin(x);

when the right hand side refers to a function that already exists?

--
Sent from: http://forum.openscad.org/

I wanted to clarify something. What if I want to write some code to operate on functions, so function foo(func) = ....; If I want to pass an existing function like sin or cos, or maybe a function, myfunc, that I defined, do I have to go through the whole syntax of foo(function(x) sin(x)) or foo(function(x,y) myfunc(x,y)) in order to do this? Or if I want to define a function variable to equal an existing function, similarly do I need to use the full syntax: func_var = function(x) sin(x); when the right hand side refers to a function that already exists? -- Sent from: http://forum.openscad.org/
TP
Torsten Paul
Sun, Nov 3, 2019 10:39 PM

On 03.11.19 22:40, adrianv wrote:

foo(function(x) sin(x))

Yes, all built-in functions and also those defined in
the previously existing function name() = expression
syntax have their own special namespace and are no values
that can be passed around. To do that they need to be
wrapped like that.

If a function literal is assigned to a variable, then
it can be passed around just like any other value too.

So:

f = function(x) x * x;

Is an unnamed function assigned to variable f, you
can assign it just like any other value to a different
variable:

g = f;

And because now g is of type function literal too:

echo(is_function(g)); // ECHO: true

It's possible to call it:

echo(g(5)); // ECHO: 25

Or pass it to a function or module:

module m(func) {
echo(func(4));
}

m(g); // ECHO: 16

ciao,
Torsten.

On 03.11.19 22:40, adrianv wrote: > foo(function(x) sin(x)) Yes, all built-in functions and also those defined in the previously existing function name() = expression syntax have their own special namespace and are no values that can be passed around. To do that they need to be wrapped like that. If a function literal is assigned to a variable, then it can be passed around just like any other value too. So: f = function(x) x * x; Is an unnamed function assigned to variable f, you can assign it just like any other value to a different variable: g = f; And because now g is of type function literal too: echo(is_function(g)); // ECHO: true It's possible to call it: echo(g(5)); // ECHO: 25 Or pass it to a function or module: module m(func) { echo(func(4)); } m(g); // ECHO: 16 ciao, Torsten.
TP
Torsten Paul
Sun, Nov 3, 2019 10:46 PM

On 03.11.19 23:46, Parkinbot wrote:

will detect a recursion. While the following typo will
indeed brick OpenSCAD

 sin = function(x) sin();
 echo(sin(30));

That should have been caught be the tail recursion limit.
I've created a ticket to investigate why the limit is
not triggered.
https://github.com/openscad/openscad/issues/3118

ciao,
Torsten.

On 03.11.19 23:46, Parkinbot wrote: > will detect a recursion. While the following typo will > indeed brick OpenSCAD > > sin = function(x) sin(); > echo(sin(30)); That should have been caught be the tail recursion limit. I've created a ticket to investigate why the limit is not triggered. https://github.com/openscad/openscad/issues/3118 ciao, Torsten.
P
Parkinbot
Sun, Nov 3, 2019 10:46 PM

Yes! Just try it out.

function foo(a, f) = f(a);
echo(foo(30, sin));

gives obviously a syntax error: "Ignoring unknown variable 'sin' ... "
while the following code does what you intended.

echo(foo(30, function(x) sin(x)));

Trying to redefine sin() by

sin = function(x) sin(x); 

in order to call

echo(foo(30, sin)); 

will detect a recursion. While the following typo will indeed brick OpenSCAD

sin = function(x) sin(); 
echo(sin(30)); 

--
Sent from: http://forum.openscad.org/

Yes! Just try it out. function foo(a, f) = f(a); echo(foo(30, sin)); gives obviously a syntax error: "Ignoring unknown variable 'sin' ... " while the following code does what you intended. echo(foo(30, function(x) sin(x))); Trying to redefine sin() by sin = function(x) sin(x); in order to call echo(foo(30, sin)); will detect a recursion. While the following typo will indeed brick OpenSCAD sin = function(x) sin(); echo(sin(30)); -- Sent from: http://forum.openscad.org/