discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Programming in Functional OpenSCAD

DM
doug moen
Fri, Feb 2, 2018 1:16 PM

Torleif: If I add 'c="TOP";' to the beginning of the program, then I get

ECHO: "TOP"
ECHO: "TOP"

which is correct behaviour for lexical scoping.

If I then wrap this entire program in a module, like so:

module top()
{
...
}
top();

then the behaviour doesn't change, so that's lexical scoping inside a
module.

On 1 February 2018 at 23:45, TLC123 torleif.ceder@gmail.com wrote:

So do I on win7. This to me indicates that scope is not nesting besides
the global. I never had any issues with this as i tend to pass parameters
with the function and i don't try to tricky around no reassign paradigm.
Obviously it would be handy to not pass huge lists down a recursion. But
once again let's remind. Openscad script is not programming it is cad
markup. There are no variables. Only constants defined with evaluated
expressions.

Sent from the OpenSCAD mailing list archive http://forum.openscad.org/
at Nabble.com.


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

Torleif: If I add 'c="TOP";' to the beginning of the program, then I get ECHO: "TOP" ECHO: "TOP" which is correct behaviour for lexical scoping. If I then wrap this entire program in a module, like so: module top() { ... } top(); then the behaviour doesn't change, so that's lexical scoping inside a module. On 1 February 2018 at 23:45, TLC123 <torleif.ceder@gmail.com> wrote: > So do I on win7. This to me indicates that scope is not nesting besides > the global. I never had any issues with this as i tend to pass parameters > with the function and i don't try to tricky around no reassign paradigm. > Obviously it would be handy to not pass huge lists down a recursion. But > once again let's remind. Openscad script is not programming it is cad > markup. There are no variables. Only constants defined with evaluated > expressions. > ------------------------------ > Sent from the OpenSCAD mailing list archive <http://forum.openscad.org/> > at Nabble.com. > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > >
SV
Simeon Veldstra
Sat, Feb 3, 2018 6:36 PM

Oh wow...
Thanks for the tip!

On 2/1/18, doug moen doug@moens.org wrote:

Thanks, Simeon. You might also want to look at Matt Keeter's libfive. It's
another implicit-function 3D modelling language, based on the Guile dialect
of Scheme. https://github.com/libfive/libfive

On 1 February 2018 at 22:21, Simeon Veldstra sim@puddle.ca wrote:

Oh, I see, "broken scoping" might be a clearer term perhaps.

The docs pretty clearly discourage reassignment at least, and the
language gives us let.

I was happy to discover OpenSCAD. Despite its warts, it suits my
style of thinking. ImplicitCAD and Ruckus were exciting to
find, but appear to be dead projects. I'm enjoying digging into
Curv, keep up the good work!

I'm quite sure you know more about language design than I do. I
find it a fascinating subject and thought my comments would be of
interest to the list.

On 2/1/18, doug moen doug@moens.org wrote:

Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as
dynamic scoping in Lisp. So I don't think that references to the theory

of

Lisp interpreters helps much.

Example 1.

1    a = 0;
2    module m()
3    {
4        function f() = a;
5        x = f();
6        a = 1;
7        y = f();
8        echo(x,y);
9    }
10  m();

Look at line 4. In a lexically scoped language, the variable name 'a'

would

statically refer to either the outer definition on line 1, or the inner
definition on line 6. One or the other. But in OpenSCAD the scope of
'a'
seems to be chosen at runtime, and varies dynamically. So the output
is:
ECHO: 0,1

Example 2.

1    a = 0;
2    function f() = a;
3    module m()
4    {
5        x = f();
6        a = 1;
7        y = f();
8        echo(x,y);
9    }
10  m();

Here, the output is "ECHO: 0,0". In this case, the variable 'a' is
lexically scoped within the function 'f'. In the previous case, the
variable 'a' was dynamically scoped within the function 'f'.

To me, this looks like a bug. It's as if the intention was to implement
lexical scoping, but the implementation is buggy. Marius has previously
stated that we can't fix this bug in a way that changes the behaviour
of
existing programs.

On 1 February 2018 at 11:37, Simeon Veldstra sim@puddle.ca wrote:

On 1/31/18, doug moen doug@moens.org wrote:
...

One caveat. In the style of language interpreter I'm familiar with,
variable and argument references are lexically scoped. In Curv, I
compile
variable and argument references into slot indexes, which index into
the
slot array of the current stack frame. So the scope of a variable
name
is
fixed at compile time, before the program is evaluated. This is
probably
why the Curv interpreter is so much faster than the OpenSCAD
interpreter:
array indexing is faster than hash table lookup. By contrast,
OpenSCAD
is
dynamically scoped. Variable and argument references are names, and
these
names are looked up dynamically during evaluation, which leads to

semantics

that are different from lexical scoping. The same variable name node

in

the

parse tree may have different scopes in different calls to the same
function or module. So you'll have to figure out how to preserve
these
dynamic scoping semantics if you implement this style of compiler,
and

I'm

not sure there are any references to help you, because the dynamic

scoping

semantics of OpenSCAD seem utterly unique to me.

Emacs lisp is another place where dynamic scope is found. This is
considered a mistake by some, and lexical scoping has been added
to recent versions.
https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding

I suppose you could index into a global table of slots to
implement dynamic scope in the compiler you describe, but it
would not be an improvement, except perhaps if your goal was
compatibility. Dynamically scoped programs are considerably
harder to reason about than lexically scoped programs. This is
true both for humans and optimizing compilers.

You can get dynamic scope by accident in an interpreter with a
slight change in how you handle the environment.

The best explanation I've seen for this is published here:
http://cs.brown.edu/courses/cs173/2012/book/From_
Substitution_to_Environments.html
In Shriram Krishnamurthi's excellent book
Programming Languages: Application and Interpretation

--
sim


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

--
sim

Oh wow... Thanks for the tip! On 2/1/18, doug moen <doug@moens.org> wrote: > Thanks, Simeon. You might also want to look at Matt Keeter's libfive. It's > another implicit-function 3D modelling language, based on the Guile dialect > of Scheme. https://github.com/libfive/libfive > > On 1 February 2018 at 22:21, Simeon Veldstra <sim@puddle.ca> wrote: > >> Oh, I see, "broken scoping" might be a clearer term perhaps. >> >> The docs pretty clearly discourage reassignment at least, and the >> language gives us let. >> >> I was happy to discover OpenSCAD. Despite its warts, it suits my >> style of thinking. ImplicitCAD and Ruckus were exciting to >> find, but appear to be dead projects. I'm enjoying digging into >> Curv, keep up the good work! >> >> I'm quite sure you know more about language design than I do. I >> find it a fascinating subject and thought my comments would be of >> interest to the list. >> >> On 2/1/18, doug moen <doug@moens.org> wrote: >> > Yes, but what I call "dynamic scoping" in OpenSCAD is not the same as >> > dynamic scoping in Lisp. So I don't think that references to the theory >> of >> > Lisp interpreters helps much. >> > >> > Example 1. >> > >> > 1 a = 0; >> > 2 module m() >> > 3 { >> > 4 function f() = a; >> > 5 x = f(); >> > 6 a = 1; >> > 7 y = f(); >> > 8 echo(x,y); >> > 9 } >> > 10 m(); >> > >> > Look at line 4. In a lexically scoped language, the variable name 'a' >> would >> > statically refer to either the outer definition on line 1, or the inner >> > definition on line 6. One or the other. But in OpenSCAD the scope of >> > 'a' >> > seems to be chosen at runtime, and varies dynamically. So the output >> > is: >> > ECHO: 0,1 >> > >> > Example 2. >> > >> > 1 a = 0; >> > 2 function f() = a; >> > 3 module m() >> > 4 { >> > 5 x = f(); >> > 6 a = 1; >> > 7 y = f(); >> > 8 echo(x,y); >> > 9 } >> > 10 m(); >> > >> > Here, the output is "ECHO: 0,0". In this case, the variable 'a' is >> > lexically scoped within the function 'f'. In the previous case, the >> > variable 'a' was dynamically scoped within the function 'f'. >> > >> > To me, this looks like a bug. It's as if the intention was to implement >> > lexical scoping, but the implementation is buggy. Marius has previously >> > stated that we can't fix this bug in a way that changes the behaviour >> > of >> > existing programs. >> > >> > On 1 February 2018 at 11:37, Simeon Veldstra <sim@puddle.ca> wrote: >> > >> >> On 1/31/18, doug moen <doug@moens.org> wrote: >> >> ... >> >> > One caveat. In the style of language interpreter I'm familiar with, >> >> > variable and argument references are lexically scoped. In Curv, I >> >> > compile >> >> > variable and argument references into slot indexes, which index into >> >> > the >> >> > slot array of the current stack frame. So the scope of a variable >> >> > name >> >> > is >> >> > fixed at compile time, before the program is evaluated. This is >> >> > probably >> >> > why the Curv interpreter is so much faster than the OpenSCAD >> >> > interpreter: >> >> > array indexing is faster than hash table lookup. By contrast, >> >> > OpenSCAD >> >> > is >> >> > dynamically scoped. Variable and argument references are names, and >> >> > these >> >> > names are looked up dynamically during evaluation, which leads to >> >> semantics >> >> > that are different from lexical scoping. The same variable name node >> in >> >> the >> >> > parse tree may have different scopes in different calls to the same >> >> > function or module. So you'll have to figure out how to preserve >> >> > these >> >> > dynamic scoping semantics if you implement this style of compiler, >> >> > and >> >> I'm >> >> > not sure there are any references to help you, because the dynamic >> >> scoping >> >> > semantics of OpenSCAD seem utterly unique to me. >> >> >> >> Emacs lisp is another place where dynamic scope is found. This is >> >> considered a mistake by some, and lexical scoping has been added >> >> to recent versions. >> >> https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBinding >> >> >> >> I suppose you could index into a global table of slots to >> >> implement dynamic scope in the compiler you describe, but it >> >> would not be an improvement, except perhaps if your goal was >> >> compatibility. Dynamically scoped programs are considerably >> >> harder to reason about than lexically scoped programs. This is >> >> true both for humans and optimizing compilers. >> >> >> >> You can get dynamic scope by accident in an interpreter with a >> >> slight change in how you handle the environment. >> >> >> >> The best explanation I've seen for this is published here: >> >> http://cs.brown.edu/courses/cs173/2012/book/From_ >> >> Substitution_to_Environments.html >> >> In Shriram Krishnamurthi's excellent book >> >> Programming Languages: Application and Interpretation >> >> >> >> >> >> -- >> >> sim >> >> >> >> _______________________________________________ >> >> OpenSCAD mailing list >> >> Discuss@lists.openscad.org >> >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> >> >> > >> >> >> -- >> sim >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >> > -- sim
R
runsun
Sat, Feb 10, 2018 1:02 AM

What is the application of a 2-3 tree in geometry, specifically in OpenSCAD?


$  Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 );   $ tips: Collection of tips on github

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

What is the application of a 2-3 tree in geometry, specifically in OpenSCAD? ----- $ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); &nbsp; $ tips: Collection of tips on github -- Sent from: http://forum.openscad.org/
N
NateTG
Sat, Feb 10, 2018 4:01 AM

runsun wrote

What is the application of a 2-3 tree in geometry, specifically in
OpenSCAD?

I did it as an exercise.  I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge lists
instead.

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

runsun wrote > What is the application of a 2-3 tree in geometry, specifically in > OpenSCAD? I did it as an exercise. I expected there to be a need for a data structure with search,insert and remove, but I guess people just munge lists instead. -- Sent from: http://forum.openscad.org/
R
runsun
Mon, Feb 12, 2018 10:53 PM

NateTG wrote

I did it as an exercise.  I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge
lists
instead.

Thx Nate. I might find it useful later when doing some geometrical calc.


$  Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 );   $ tips: Collection of tips on github

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

NateTG wrote > I did it as an exercise. I expected there to be a need for a data > structure with search,insert and remove, but I guess people just munge > lists > instead. Thx Nate. I might find it useful later when doing some geometrical calc. ----- $ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); &nbsp; $ tips: Collection of tips on github -- Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Mon, Feb 12, 2018 11:49 PM

A 2-3 tree is an efficient balanced tree and it might be helpful to store
geometrical data that should be sorted for fast retrieval. To be useful, a
2-3 tree data structure should store a register containing the relevant
geometrical data besides the sorting key. A simple example of that is the
quicksort method that is found in the OpenSCAD manual [1]. It allows to
sort an array of numbers. However, it is useless to sort a list of points
by its first coordinates a much more useful task. I had to write my own
version.

[1]
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_Comprehensions#Sorting_a_vector

2018-02-12 20:53 GMT-02:00 runsun runsun@gmail.com:

NateTG wrote

I did it as an exercise.  I expected there to be a need for a data
structure with search,insert and remove, but I guess people just munge
lists
instead.

Thx Nate. I might find it useful later when doing some geometrical calc.

A 2-3 tree is an efficient balanced tree and it might be helpful to store geometrical data that should be sorted for fast retrieval. To be useful, a 2-3 tree data structure should store a register containing the relevant geometrical data besides the sorting key. A simple example of that is the quicksort method that is found in the OpenSCAD manual [1]. It allows to sort an array of numbers. However, it is useless to sort a list of points by its first coordinates a much more useful task. I had to write my own version. [1] https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/List_Comprehensions#Sorting_a_vector 2018-02-12 20:53 GMT-02:00 runsun <runsun@gmail.com>: > NateTG wrote > > I did it as an exercise. I expected there to be a need for a data > > structure with search,insert and remove, but I guess people just munge > > lists > > instead. > > Thx Nate. I might find it useful later when doing some geometrical calc. >
N
NateTG
Tue, Feb 13, 2018 1:30 AM

Yeah, it would be nice if there were a way to make the tree generic so that
it works with many different comparison functions.

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

Yeah, it would be nice if there were a way to make the tree generic so that it works with many different comparison functions. -- Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Tue, Feb 13, 2018 2:30 AM

Yeah, it would be nice if there were a way to make the tree generic so that
it works with many different comparison functions.

Since we don't have first class functions in OpenSCAD, a simple alternative
solution would be to insert arrays in the tree: by convention the sorting
key is the first element of the array and the remaining is something to be
interpreted by the calling code. Like, for instance:

function quick_sort(arr, simple=true) =
!(len(arr)>0) ? [] :
let( pivot  = simple ? arr[floor(len(arr)/2)] :
arr[floor(len(arr)/2)][0],
lesser  = [ for (y = arr) if ( (simple ?  y : y[0])  < pivot ) y ],
equal  = [ for (y = arr) if ( (simple ?  y : y[0]) == pivot ) y ],
greater = [ for (y = arr) if ( (simple ?  y : y[0])  > pivot ) y ]
)
concat( quick_sort(lesser,  simple),
equal,
quick_sort(greater, simple) );

l = [ 5,2,8,0,4,7 ]; // simple list of numbers
echo(quick_sort(l));
// ECHO: [0, 2, 4, 5, 7, 8]
v = [ [-1,2], [-7,[4]], [-9,2,4,5], [-3] ]; // list of non void lists
echo(quick_sort(v, simple=false));
// ECHO: [[-9, 2, 4, 5], [-7, [4]], [-3], [-1, 2]]

​ > Yeah, it would be nice if there were a way to make the tree generic so that > it works with many different comparison functions. Since we don't have first class functions in OpenSCAD, a simple alternative solution would be to insert arrays in the tree: by convention the sorting key is the first element of the array and the remaining is something to be interpreted by the calling code. Like, for instance: function quick_sort(arr, simple=true) = !(len(arr)>0) ? [] : let( pivot = simple ? arr[floor(len(arr)/2)] : arr[floor(len(arr)/2)][0], lesser = [ for (y = arr) if ( (simple ? y : y[0]) < pivot ) y ], equal = [ for (y = arr) if ( (simple ? y : y[0]) == pivot ) y ], greater = [ for (y = arr) if ( (simple ? y : y[0]) > pivot ) y ] ) concat( quick_sort(lesser, simple), equal, quick_sort(greater, simple) ); l = [ 5,2,8,0,4,7 ]; // simple list of numbers echo(quick_sort(l)); // ECHO: [0, 2, 4, 5, 7, 8] v = [ [-1,2], [-7,[4]], [-9,2,4,5], [-3] ]; // list of non void lists echo(quick_sort(v, simple=false)); // ECHO: [[-9, 2, 4, 5], [-7, [4]], [-3], [-1, 2]]
R
runsun
Tue, Feb 13, 2018 5:50 PM

@Ronaldo, nice code. It's very fast -- sorting 100,000 pts 10 times cost only
6 sec.  Pulling the 'simple?' check out of the loops, like this:

<blockquote> function quick_sort_1(arr, simple=true) = ( !(len(arr)>0) ? [] : let( pivot = simple ? arr[floor(len(arr)/2)] : arr[floor(len(arr)/2)][0], lesser = simple? [ for (y = arr) if ( y < pivot ) y ] : [ for (y = arr) if ( y[0] < pivot ) y ], , equal = simple? [ for (y = arr) if ( y == pivot ) y ] : [ for (y = arr) if ( y[0] == pivot ) y ] , greater = simple? [ for (y = arr) if ( y > pivot ) y ] : [ for (y = arr) if ( y[0] > pivot ) y ] ) concat( quick_sort_1(lesser, simple), equal, quick_sort_1(greater, simple) ) ); </blockquote>

gained a little speed (100,000 x 10 => 5 sec) but i guess even in extreme
use of OpenSCAD it doesn't matter.


$  Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 );   $ tips: Collection of tips on github

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

@Ronaldo, nice code. It's very fast -- sorting 100,000 pts 10 times cost only 6 sec. Pulling the 'simple?' check out of the loops, like this: <blockquote> function quick_sort_1(arr, simple=true) = ( !(len(arr)>0) ? [] : let( pivot = simple ? arr[floor(len(arr)/2)] : arr[floor(len(arr)/2)][0], lesser = simple? [ for (y = arr) if ( y < pivot ) y ] : [ for (y = arr) if ( y[0] < pivot ) y ], , equal = simple? [ for (y = arr) if ( y == pivot ) y ] : [ for (y = arr) if ( y[0] == pivot ) y ] , greater = simple? [ for (y = arr) if ( y > pivot ) y ] : [ for (y = arr) if ( y[0] > pivot ) y ] ) concat( quick_sort_1(lesser, simple), equal, quick_sort_1(greater, simple) ) ); </blockquote> gained a little speed (100,000 x 10 => 5 sec) but i guess even in extreme use of OpenSCAD it doesn't matter. ----- $ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); &nbsp; $ tips: Collection of tips on github -- Sent from: http://forum.openscad.org/
R
runsun
Tue, Feb 13, 2018 6:13 PM

Inspired by Ronaldo's code, here is a more generalized quick sort for arrays:
<br/>

<blockquote> function sortArrs(arrs, by=0)= ( !(len(arrs)>0) ? [] : let( pivot = arrs[floor(len(arrs)/2)][by], lesser = [ for (y = arrs) if ( y[by] < pivot ) y ], , equal = [ for (y = arrs) if ( y[by] == pivot ) y ] , greater = [ for (y = arrs) if ( y[by] > pivot ) y ] ) concat( sortArrs(lesser, by), equal, sortArrs(greater, by) ) );</blockquote>

/*
ECHO: [[2.9, 1.9, -2.8], [-2.5, -1.5, -2.9], [-0.1, 2.8, 2.4], [0.9, 1.8,
0.8], [-1, -1.3, -0.8]]

[[-2.5, -1.5, -2.9]
, [-1, -1.3, -0.8]
, [-0.1, 2.8, 2.4]
, [0.9, 1.8, 0.8]
, [2.9, 1.9, -2.8]]

[[-2.5, -1.5, -2.9]
, [-1, -1.3, -0.8]
, [0.9, 1.8, 0.8]
, [2.9, 1.9, -2.8]
, [-0.1, 2.8, 2.4]
]

[[-2.5, -1.5, -2.9]
, [2.9, 1.9, -2.8]
, [-1, -1.3, -0.8]
, [0.9, 1.8, 0.8]
, [-0.1, 2.8, 2.4]]

*/


$  Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 );   $ tips: Collection of tips on github

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

Inspired by Ronaldo's code, here is a more generalized quick sort for arrays: <br/> <blockquote> function sortArrs(arrs, by=0)= ( !(len(arrs)>0) ? [] : let( pivot = arrs[floor(len(arrs)/2)][by], lesser = [ for (y = arrs) if ( y[by] < pivot ) y ], , equal = [ for (y = arrs) if ( y[by] == pivot ) y ] , greater = [ for (y = arrs) if ( y[by] > pivot ) y ] ) concat( sortArrs(lesser, by), equal, sortArrs(greater, by) ) );</blockquote> /* ECHO: [[2.9, 1.9, -2.8], [-2.5, -1.5, -2.9], [-0.1, 2.8, 2.4], [0.9, 1.8, 0.8], [-1, -1.3, -0.8]] [[-2.5, -1.5, -2.9] , [-1, -1.3, -0.8] , [-0.1, 2.8, 2.4] , [0.9, 1.8, 0.8] , [2.9, 1.9, -2.8]] [[-2.5, -1.5, -2.9] , [-1, -1.3, -0.8] , [0.9, 1.8, 0.8] , [2.9, 1.9, -2.8] , [-0.1, 2.8, 2.4] ] [[-2.5, -1.5, -2.9] , [2.9, 1.9, -2.8] , [-1, -1.3, -0.8] , [0.9, 1.8, 0.8] , [-0.1, 2.8, 2.4]] */ ----- $ Runsun Pan, PhD $ libs: doctest , faces ( git ), offline doc ( git ), runscad.py ( 2 , git ), synwrite ( 2 ); &nbsp; $ tips: Collection of tips on github -- Sent from: http://forum.openscad.org/