A
adrianv
Sun, Aug 16, 2020 5:57 PM
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance that
an operation is illegal. There's no obvious way to determine if a variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v produces
an immediate warning for lists that aren't vectors. So if this is staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of the
same length).
--
Sent from: http://forum.openscad.org/
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance that
an operation is illegal. There's no obvious way to determine if a variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v produces
an immediate warning for lists that aren't vectors. So if this is staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of the
same length).
--
Sent from: http://forum.openscad.org/
JB
Jordan Brown
Sun, Aug 16, 2020 6:41 PM
On 8/16/2020 10:57 AM, adrianv wrote:
The one issue this raises is that it's not very easy to tell in advance that
an operation is illegal. There's no obvious way to determine if a variable
is a vector, or a matrix, for example. [...]
Um... not obvious for whom? And why?
X = [ 1,2,3 ];
Y = 7;
X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You
know what types the variables are when you set them. You need to keep
track of types as they flow through the program. At any given step,
that's easy. When you make a mistake, you get an error.
This is generally easy in a language that's got strong static typing; if
you have a mismatch, the compiler complains, and it's checked at every
reference.
It's harder in a language like OpenSCAD that I would describe as
strongly typed (little automatic conversion) but dynamically typed (the
type isn't checked until you actually try to execute an operation). You
need naming and commenting conventions, and you need to pay attention.[*]
If you're trying to detect an illegal operation... why? If the inputs
are the wrong type, then they are the wrong type and an error is
appropriate.
I come up with two answers:
- You want better or earlier errors. Rather than getting an error
from six levels down where the illegal operation finally happened,
you want an error at the top that says "Hey, bozo, you passed a
string for the length and it needs to be a number". Pretty much,
you want a type assertion; you would actually have been happier if
there had been a static typing mechanism so that the infrastructure
would do the check for you.
- You want to implement polymorphism, where two or more types are
allowed and yield different (though presumably similar) results.
For instance, rotate() is polymorphic - you can pass it either a
single number (interpreted as a rotation around the Z axis) or an
array of [x,y,z] rotations. Note that this isn't about detecting an
illegal operation - it's about determining the type of a value so
as to process it in an appropriate way.
Exactly what problem are you trying to solve?
[*] <rant>This is why I think the current tendency towards dynamic
typing and weak typing, as exemplified by languages like JavaScript,
is simply awful in the long term. (Python is better in simple
cases, but no better in complex cases.) Debugging large programs is
very difficult and programmers need every bit of automated
assistance that they can get. Dynamic typing and weak typing make
the language look simpler at the start, but cripple your ability to
statically analyze it and detect bugs before actually executing the
program.</rant>
On 8/16/2020 10:57 AM, adrianv wrote:
> The one issue this raises is that it's not very easy to tell in advance that
> an operation is illegal. There's no obvious way to determine if a variable
> is a vector, or a matrix, for example. [...]
Um... not obvious for whom? And why?
X = [ 1,2,3 ];
Y = 7;
X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You
know what types the variables are when you set them. You need to keep
track of types as they flow through the program. At any given step,
that's easy. When you make a mistake, you get an error.
This is generally easy in a language that's got strong static typing; if
you have a mismatch, the compiler complains, and it's checked at every
reference.
It's harder in a language like OpenSCAD that I would describe as
strongly typed (little automatic conversion) but dynamically typed (the
type isn't checked until you actually try to execute an operation). You
need naming and commenting conventions, and you need to pay attention.[*]
If you're trying to detect an illegal operation... why? If the inputs
are the wrong type, then they are the wrong type and an error is
appropriate.
I come up with two answers:
* You want better or earlier errors. Rather than getting an error
from six levels down where the illegal operation finally happened,
you want an error at the top that says "Hey, bozo, you passed a
string for the length and it needs to be a number". Pretty much,
you want a type assertion; you would actually have been happier if
there had been a static typing mechanism so that the infrastructure
would do the check for you.
* You want to implement polymorphism, where two or more types are
allowed and yield different (though presumably similar) results.
For instance, rotate() is polymorphic - you can pass it either a
single number (interpreted as a rotation around the Z axis) or an
array of [x,y,z] rotations. Note that this isn't about detecting an
*illegal* operation - it's about determining the type of a value so
as to process it in an appropriate way.
Exactly what problem are you trying to solve?
---
[*] <rant>This is why I think the current tendency towards dynamic
typing and weak typing, as exemplified by languages like JavaScript,
is simply awful in the long term. (Python is better in simple
cases, but no better in complex cases.) Debugging large programs is
very difficult and programmers need every bit of automated
assistance that they can get. Dynamic typing and weak typing make
the language look simpler at the start, but cripple your ability to
statically analyze it and detect bugs before actually executing the
program.</rant>
HL
Hans L
Sun, Aug 16, 2020 6:57 PM
These are still fairly simple functions to implement, and much clearer IMO
to explicitly check your types than trying a possibly undefined operation
to see if it sticks:
// check if a value is a list that contains only numbers (empty list
doesn't count)
function is_vecnum(v) = is_list(v) && len(v) && len([for(x=v)
if(!is_num(x)) 0]) == 0;
// check if a value is a list that is n elements long and all numbers:
function is_vecn(v,n) = is_list(v) && len(v)==n && len([for(x=v)
if(!is_num(x)) 0]) == 0;
// check if a value is a list of lists of numbers, all the same length
(again empty list, and list of empty lists don't count)
function is_matrix(m) = is_list(m) && len(m) && is_list(m[0]) &&
(let(n=len(m[0])) n && len([for(v=m) if(!is_vecn(v,n)) 0]) == 0);
On Sun, Aug 16, 2020 at 12:58 PM adrianv avm4@cornell.edu wrote:
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid
operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance
that
an operation is illegal. There's no obvious way to determine if a
variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v produces
an immediate warning for lists that aren't vectors. So if this is staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of the
same length).
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
These are still fairly simple functions to implement, and much clearer IMO
to explicitly check your types than trying a possibly undefined operation
to see if it sticks:
// check if a value is a list that contains only numbers (empty list
doesn't count)
function is_vecnum(v) = is_list(v) && len(v) && len([for(x=v)
if(!is_num(x)) 0]) == 0;
// check if a value is a list that is n elements long and all numbers:
function is_vecn(v,n) = is_list(v) && len(v)==n && len([for(x=v)
if(!is_num(x)) 0]) == 0;
// check if a value is a list of lists of numbers, all the same length
(again empty list, and list of empty lists don't count)
function is_matrix(m) = is_list(m) && len(m) && is_list(m[0]) &&
(let(n=len(m[0])) n && len([for(v=m) if(!is_vecn(v,n)) 0]) == 0);
On Sun, Aug 16, 2020 at 12:58 PM adrianv <avm4@cornell.edu> wrote:
> I briefly tested the dev version from Aug 8 and found that the feature
> discussed in another thread appears there, but not quite in the form
> described in that thread.
>
> The description in the other thread was that illegal operations that
> currently produce undef would generate a warning, but not until the undef
> was used in an operation. It seems that in this version, invalid
> operations
> such as illegal matrix or vector products immediately produce a warning.
>
> The one issue this raises is that it's not very easy to tell in advance
> that
> an operation is illegal. There's no obvious way to determine if a
> variable
> is a vector, or a matrix, for example. We used to detect vectors with
>
> is_list(v) && is_num(0*(v*v))
>
> but this produces an immediate warning now, and in fact simply v*v produces
> an immediate warning for lists that aren't vectors. So if this is staying
> this way, then I'm wondering if we can have a mechanism for determining
> validity of list data, like is_vector that is true only for a list of
> numbers, or is_matrix that is true only for a matrix (list of lists of the
> same length).
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
RD
Revar Desmera
Sun, Aug 16, 2020 7:12 PM
First off, when writing a library, you must assume your users will pass you utter garbage sometimes, and you need to call them out on that.
Second, yes polymorphism is also very useful.
Third, these “type” checks need to be as fast as possible because some routines that call them may be called very very often themselves.
-Revar
On Aug 16, 2020, at 11:42 AM, Jordan Brown openscad@jordan.maileater.net wrote:
X = [ 1,2,3 ];
Y = 7;
X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You know what types the variables are when you set them. You need to keep track of types as they flow through the program. At any given step, that's easy. When you make a mistake, you get an error.
First off, when writing a library, you must assume your users will pass you utter garbage sometimes, and you need to call them out on that.
Second, yes polymorphism is also very useful.
Third, these “type” checks need to be as fast as possible because some routines that call them may be called very very often themselves.
-Revar
> On Aug 16, 2020, at 11:42 AM, Jordan Brown <openscad@jordan.maileater.net> wrote:
>
>
> X = [ 1,2,3 ];
> Y = 7;
> X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You know what types the variables are when you set them. You need to keep track of types as they flow through the program. At any given step, that's easy. When you make a mistake, you get an error.
RD
Revar Desmera
Sun, Aug 16, 2020 7:17 PM
We used to use something like the methods below. They were far slower. A LOT slower than (ab)using the built in dot and vector multiplication.
Since these routines get called a lot in our higher level code, speed is important.
-Revar
On Aug 16, 2020, at 11:58 AM, Hans L thehans@gmail.com wrote:
These are still fairly simple functions to implement, and much clearer IMO to explicitly check your types than trying a possibly undefined operation to see if it sticks:
// check if a value is a list that contains only numbers (empty list doesn't count)
function is_vecnum(v) = is_list(v) && len(v) && len([for(x=v) if(!is_num(x)) 0]) == 0;
// check if a value is a list that is n elements long and all numbers:
function is_vecn(v,n) = is_list(v) && len(v)==n && len([for(x=v) if(!is_num(x)) 0]) == 0;
// check if a value is a list of lists of numbers, all the same length (again empty list, and list of empty lists don't count)
function is_matrix(m) = is_list(m) && len(m) && is_list(m[0]) && (let(n=len(m[0])) n && len([for(v=m) if(!is_vecn(v,n)) 0]) == 0);
On Sun, Aug 16, 2020 at 12:58 PM adrianv avm4@cornell.edu wrote:
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance that
an operation is illegal. There's no obvious way to determine if a variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v produces
an immediate warning for lists that aren't vectors. So if this is staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of the
same length).
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
We used to use something like the methods below. They were far slower. A LOT slower than (ab)using the built in dot and vector multiplication.
Since these routines get called a lot in our higher level code, speed is important.
-Revar
> On Aug 16, 2020, at 11:58 AM, Hans L <thehans@gmail.com> wrote:
>
>
> These are still fairly simple functions to implement, and much clearer IMO to explicitly check your types than trying a possibly undefined operation to see if it sticks:
>
> // check if a value is a list that contains only numbers (empty list doesn't count)
> function is_vecnum(v) = is_list(v) && len(v) && len([for(x=v) if(!is_num(x)) 0]) == 0;
>
> // check if a value is a list that is n elements long and all numbers:
> function is_vecn(v,n) = is_list(v) && len(v)==n && len([for(x=v) if(!is_num(x)) 0]) == 0;
>
> // check if a value is a list of lists of numbers, all the same length (again empty list, and list of empty lists don't count)
> function is_matrix(m) = is_list(m) && len(m) && is_list(m[0]) && (let(n=len(m[0])) n && len([for(v=m) if(!is_vecn(v,n)) 0]) == 0);
>
>
>> On Sun, Aug 16, 2020 at 12:58 PM adrianv <avm4@cornell.edu> wrote:
>> I briefly tested the dev version from Aug 8 and found that the feature
>> discussed in another thread appears there, but not quite in the form
>> described in that thread.
>>
>> The description in the other thread was that illegal operations that
>> currently produce undef would generate a warning, but not until the undef
>> was used in an operation. It seems that in this version, invalid operations
>> such as illegal matrix or vector products immediately produce a warning.
>>
>> The one issue this raises is that it's not very easy to tell in advance that
>> an operation is illegal. There's no obvious way to determine if a variable
>> is a vector, or a matrix, for example. We used to detect vectors with
>>
>> is_list(v) && is_num(0*(v*v))
>>
>> but this produces an immediate warning now, and in fact simply v*v produces
>> an immediate warning for lists that aren't vectors. So if this is staying
>> this way, then I'm wondering if we can have a mechanism for determining
>> validity of list data, like is_vector that is true only for a list of
>> numbers, or is_matrix that is true only for a matrix (list of lists of the
>> same length).
>>
>>
>>
>> --
>> Sent from: http://forum.openscad.org/
>>
>> _______________________________________________
>> OpenSCAD mailing list
>> Discuss@lists.openscad.org
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
A
adrianv
Sun, Aug 16, 2020 7:18 PM
You're actually trivializing that task of determining if a variable holds a
vector. How can I possibly know if
X = f(1,2,3);
leaves X set equal to a vector or not? I may think I've done everything
right and f is a vector, but I could be wrong. The function f could do all
sorts of complicated things. It could have issues with its parameters which
might be out of bounds, causing it to produce a misformed answer. It could
have a bug and produce by accident [[1,2],3,4]] instead of [1,2,3,4]. In
fact, technically speaking there is no algorithmic method to look at the
code and know that X is a vector, because that problem is equivalent to the
halting problem, which is undecidable.
So what I'm talking about is validating the inputs to a module or function.
If I write a function and it needs a vector and some other person uses it
and they get a cryptic error when I try to use the vector it's not the best
result. It's actually not the best result for me either: is it a bug in my
module or an error in how I called it? So my module might look like
module foo(v) {
assert(is_vector(v), "Parameter v must be a vector.");
...
}
Even in code I wrote myself, without this sort of error handling it is
significantly harder to debug complex code. I say this from personal
experience. But if someone else, who doesn't understand how the code works
internally, uses my code then it becomes a much greater challenge to debug
errors where you have passed mismatched parameters. It's basically just
correct programming to actually check that inputs are valid before trying to
use them.
Another thing is that my code may want to do something different if the
input is a vector or a matrix, which is another reason for wanting to
distinguish these from each other.
So how do I write the is_vector() function in such a way that is efficient,
even if the vector has length 100k, for example? Or if I call is_vector
many times? If I can't use the v*v trick then I'm probably stuck with
something much slower like
function is_vector(v) = // note code not
tested
let(bad = [for(entry=v) if !is_num(entry) 1])
bad==[];
And more difficult: how do you write is_matrix()? The naive method is
going to be to loop over the list and call is_vector on every list entry and
also check that they are all the same length. This is going to be slow. A
faster method to test if A is a matrix is to compute A*A[0] and check that
it's a vector.
JordanBrown wrote
On 8/16/2020 10:57 AM, adrianv wrote:
The one issue this raises is that it's not very easy to tell in advance
that
an operation is illegal. There's no obvious way to determine if a
variable
is a vector, or a matrix, for example. [...]
Um... not obvious for whom? And why?
X = [ 1,2,3 ];
Y = 7;
X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You
know what types the variables are when you set them. You need to keep
track of types as they flow through the program. At any given step,
that's easy. When you make a mistake, you get an error.
This is generally easy in a language that's got strong static typing; if
you have a mismatch, the compiler complains, and it's checked at every
reference.
It's harder in a language like OpenSCAD that I would describe as
strongly typed (little automatic conversion) but dynamically typed (the
type isn't checked until you actually try to execute an operation). You
need naming and commenting conventions, and you need to pay attention.[*]
If you're trying to detect an illegal operation... why? If the inputs
are the wrong type, then they are the wrong type and an error is
appropriate.
I come up with two answers:
- You want better or earlier errors. Rather than getting an error
from six levels down where the illegal operation finally happened,
you want an error at the top that says "Hey, bozo, you passed a
string for the length and it needs to be a number". Pretty much,
you want a type assertion; you would actually have been happier if
there had been a static typing mechanism so that the infrastructure
would do the check for you.
- You want to implement polymorphism, where two or more types are
allowed and yield different (though presumably similar) results.
For instance, rotate() is polymorphic - you can pass it either a
single number (interpreted as a rotation around the Z axis) or an
array of [x,y,z] rotations. Note that this isn't about detecting an
illegal operation - it's about determining the type of a value so
as to process it in an appropriate way.
Exactly what problem are you trying to solve?
[*]
<rant>
This is why I think the current tendency towards dynamic
typing and weak typing, as exemplified by languages like JavaScript,
is simply awful in the long term. (Python is better in simple
cases, but no better in complex cases.) Debugging large programs is
very difficult and programmers need every bit of automated
assistance that they can get. Dynamic typing and weak typing make
the language look simpler at the start, but cripple your ability to
statically analyze it and detect bugs before actually executing the
program.
</rant>
OpenSCAD mailing list
You're actually trivializing that task of determining if a variable holds a
vector. How can I possibly know if
X = f(1,2,3);
leaves X set equal to a vector or not? I may *think* I've done everything
right and f is a vector, but I could be wrong. The function f could do all
sorts of complicated things. It could have issues with its parameters which
might be out of bounds, causing it to produce a misformed answer. It could
have a bug and produce by accident [[1,2],3,4]] instead of [1,2,3,4]. In
fact, technically speaking there is no algorithmic method to look at the
code and know that X is a vector, because that problem is equivalent to the
halting problem, which is undecidable.
So what I'm talking about is validating the inputs to a module or function.
If I write a function and it needs a vector and some other person uses it
and they get a cryptic error when I try to use the vector it's not the best
result. It's actually not the best result for me either: is it a bug in my
module or an error in how I called it? So my module might look like
module foo(v) {
assert(is_vector(v), "Parameter v must be a vector.");
...
}
Even in code I wrote myself, without this sort of error handling it is
significantly harder to debug complex code. I say this from personal
experience. But if someone else, who doesn't understand how the code works
internally, uses my code then it becomes a much greater challenge to debug
errors where you have passed mismatched parameters. It's basically just
correct programming to actually check that inputs are valid before trying to
use them.
Another thing is that my code may want to do something different if the
input is a vector or a matrix, which is another reason for wanting to
distinguish these from each other.
So how do I write the is_vector() function in such a way that is efficient,
even if the vector has length 100k, for example? Or if I call is_vector
many times? If I can't use the v*v trick then I'm probably stuck with
something much slower like
function is_vector(v) = // note code not
tested
let(bad = [for(entry=v) if !is_num(entry) 1])
bad==[];
And more difficult: how do you write is_matrix()? The naive method is
going to be to loop over the list and call is_vector on every list entry and
also check that they are all the same length. This is going to be slow. A
faster method to test if A is a matrix is to compute A*A[0] and check that
it's a vector.
JordanBrown wrote
> On 8/16/2020 10:57 AM, adrianv wrote:
>> The one issue this raises is that it's not very easy to tell in advance
>> that
>> an operation is illegal. There's no obvious way to determine if a
>> variable
>> is a vector, or a matrix, for example. [...]
>
> Um... not obvious for whom? And why?
>
> X = [ 1,2,3 ];
> Y = 7;
>
> X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You
> know what types the variables are when you set them. You need to keep
> track of types as they flow through the program. At any given step,
> that's easy. When you make a mistake, you get an error.
>
> This is generally easy in a language that's got strong static typing; if
> you have a mismatch, the compiler complains, and it's checked at every
> reference.
>
> It's harder in a language like OpenSCAD that I would describe as
> strongly typed (little automatic conversion) but dynamically typed (the
> type isn't checked until you actually try to execute an operation). You
> need naming and commenting conventions, and you need to pay attention.[*]
>
> If you're trying to detect an illegal operation... why? If the inputs
> are the wrong type, then they are the wrong type and an error is
> appropriate.
>
> I come up with two answers:
>
> * You want better or earlier errors. Rather than getting an error
> from six levels down where the illegal operation finally happened,
> you want an error at the top that says "Hey, bozo, you passed a
> string for the length and it needs to be a number". Pretty much,
> you want a type assertion; you would actually have been happier if
> there had been a static typing mechanism so that the infrastructure
> would do the check for you.
> * You want to implement polymorphism, where two or more types are
> allowed and yield different (though presumably similar) results.
> For instance, rotate() is polymorphic - you can pass it either a
> single number (interpreted as a rotation around the Z axis) or an
> array of [x,y,z] rotations. Note that this isn't about detecting an
> *illegal* operation - it's about determining the type of a value so
> as to process it in an appropriate way.
>
> Exactly what problem are you trying to solve?
>
> ---
>
> [*]
> <rant>
> This is why I think the current tendency towards dynamic
> typing and weak typing, as exemplified by languages like JavaScript,
> is simply awful in the long term. (Python is better in simple
> cases, but no better in complex cases.) Debugging large programs is
> very difficult and programmers need every bit of automated
> assistance that they can get. Dynamic typing and weak typing make
> the language look simpler at the start, but cripple your ability to
> statically analyze it and detect bugs before actually executing the
> program.
> </rant>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@.openscad
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
HL
Hans L
Sun, Aug 16, 2020 7:33 PM
Assuming the checks will be too slow sounds like premature optimization to
me.
Do you have a real world use case (something actually used to create
geometry and not just designed specifically to be difficult) where the
functions I posted above for example are unacceptably slow?
On Sun, Aug 16, 2020 at 2:19 PM adrianv avm4@cornell.edu wrote:
You're actually trivializing that task of determining if a variable holds a
vector. How can I possibly know if
X = f(1,2,3);
leaves X set equal to a vector or not? I may think I've done everything
right and f is a vector, but I could be wrong. The function f could do
all
sorts of complicated things. It could have issues with its parameters
which
might be out of bounds, causing it to produce a misformed answer. It could
have a bug and produce by accident [[1,2],3,4]] instead of [1,2,3,4]. In
fact, technically speaking there is no algorithmic method to look at the
code and know that X is a vector, because that problem is equivalent to the
halting problem, which is undecidable.
So what I'm talking about is validating the inputs to a module or
function.
If I write a function and it needs a vector and some other person uses it
and they get a cryptic error when I try to use the vector it's not the best
result. It's actually not the best result for me either: is it a bug in my
module or an error in how I called it? So my module might look like
module foo(v) {
assert(is_vector(v), "Parameter v must be a vector.");
...
}
Even in code I wrote myself, without this sort of error handling it is
significantly harder to debug complex code. I say this from personal
experience. But if someone else, who doesn't understand how the code works
internally, uses my code then it becomes a much greater challenge to debug
errors where you have passed mismatched parameters. It's basically just
correct programming to actually check that inputs are valid before trying
to
use them.
Another thing is that my code may want to do something different if the
input is a vector or a matrix, which is another reason for wanting to
distinguish these from each other.
So how do I write the is_vector() function in such a way that is efficient,
even if the vector has length 100k, for example? Or if I call is_vector
many times? If I can't use the v*v trick then I'm probably stuck with
something much slower like
function is_vector(v) = // note code not
tested
let(bad = [for(entry=v) if !is_num(entry) 1])
bad==[];
And more difficult: how do you write is_matrix()? The naive method is
going to be to loop over the list and call is_vector on every list entry
and
also check that they are all the same length. This is going to be slow.
A
faster method to test if A is a matrix is to compute A*A[0] and check that
it's a vector.
JordanBrown wrote
On 8/16/2020 10:57 AM, adrianv wrote:
The one issue this raises is that it's not very easy to tell in advance
that
an operation is illegal. There's no obvious way to determine if a
variable
is a vector, or a matrix, for example. [...]
Um... not obvious for whom? And why?
X = [ 1,2,3 ];
Y = 7;
X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You
know what types the variables are when you set them. You need to keep
track of types as they flow through the program. At any given step,
that's easy. When you make a mistake, you get an error.
This is generally easy in a language that's got strong static typing; if
you have a mismatch, the compiler complains, and it's checked at every
reference.
It's harder in a language like OpenSCAD that I would describe as
strongly typed (little automatic conversion) but dynamically typed (the
type isn't checked until you actually try to execute an operation). You
need naming and commenting conventions, and you need to pay attention.[*]
If you're trying to detect an illegal operation... why? If the inputs
are the wrong type, then they are the wrong type and an error is
appropriate.
I come up with two answers:
- You want better or earlier errors. Rather than getting an error
from six levels down where the illegal operation finally happened,
you want an error at the top that says "Hey, bozo, you passed a
string for the length and it needs to be a number". Pretty much,
you want a type assertion; you would actually have been happier if
there had been a static typing mechanism so that the infrastructure
would do the check for you.
- You want to implement polymorphism, where two or more types are
allowed and yield different (though presumably similar) results.
For instance, rotate() is polymorphic - you can pass it either a
single number (interpreted as a rotation around the Z axis) or an
array of [x,y,z] rotations. Note that this isn't about detecting an
illegal operation - it's about determining the type of a value so
as to process it in an appropriate way.
Exactly what problem are you trying to solve?
[*]
<rant>
This is why I think the current tendency towards dynamic
typing and weak typing, as exemplified by languages like JavaScript,
is simply awful in the long term. (Python is better in simple
cases, but no better in complex cases.) Debugging large programs is
very difficult and programmers need every bit of automated
assistance that they can get. Dynamic typing and weak typing make
the language look simpler at the start, but cripple your ability to
statically analyze it and detect bugs before actually executing the
program.
</rant>
OpenSCAD mailing list
Assuming the checks will be too slow sounds like premature optimization to
me.
Do you have a real world use case (something actually used to create
geometry and not just designed specifically to be difficult) where the
functions I posted above for example are unacceptably slow?
On Sun, Aug 16, 2020 at 2:19 PM adrianv <avm4@cornell.edu> wrote:
> You're actually trivializing that task of determining if a variable holds a
> vector. How can I possibly know if
>
> X = f(1,2,3);
>
> leaves X set equal to a vector or not? I may *think* I've done everything
> right and f is a vector, but I could be wrong. The function f could do
> all
> sorts of complicated things. It could have issues with its parameters
> which
> might be out of bounds, causing it to produce a misformed answer. It could
> have a bug and produce by accident [[1,2],3,4]] instead of [1,2,3,4]. In
> fact, technically speaking there is no algorithmic method to look at the
> code and know that X is a vector, because that problem is equivalent to the
> halting problem, which is undecidable.
>
> So what I'm talking about is validating the inputs to a module or
> function.
> If I write a function and it needs a vector and some other person uses it
> and they get a cryptic error when I try to use the vector it's not the best
> result. It's actually not the best result for me either: is it a bug in my
> module or an error in how I called it? So my module might look like
>
> module foo(v) {
> assert(is_vector(v), "Parameter v must be a vector.");
> ...
> }
>
> Even in code I wrote myself, without this sort of error handling it is
> significantly harder to debug complex code. I say this from personal
> experience. But if someone else, who doesn't understand how the code works
> internally, uses my code then it becomes a much greater challenge to debug
> errors where you have passed mismatched parameters. It's basically just
> correct programming to actually check that inputs are valid before trying
> to
> use them.
>
> Another thing is that my code may want to do something different if the
> input is a vector or a matrix, which is another reason for wanting to
> distinguish these from each other.
>
> So how do I write the is_vector() function in such a way that is efficient,
> even if the vector has length 100k, for example? Or if I call is_vector
> many times? If I can't use the v*v trick then I'm probably stuck with
> something much slower like
>
> function is_vector(v) = // note code not
> tested
> let(bad = [for(entry=v) if !is_num(entry) 1])
> bad==[];
>
> And more difficult: how do you write is_matrix()? The naive method is
> going to be to loop over the list and call is_vector on every list entry
> and
> also check that they are all the same length. This is going to be slow.
> A
> faster method to test if A is a matrix is to compute A*A[0] and check that
> it's a vector.
>
>
>
> JordanBrown wrote
> > On 8/16/2020 10:57 AM, adrianv wrote:
> >> The one issue this raises is that it's not very easy to tell in advance
> >> that
> >> an operation is illegal. There's no obvious way to determine if a
> >> variable
> >> is a vector, or a matrix, for example. [...]
> >
> > Um... not obvious for whom? And why?
> >
> > X = [ 1,2,3 ];
> > Y = 7;
> >
> > X is an array. Y is a number. X+1 is illegal. Y[0] is illegal. You
> > know what types the variables are when you set them. You need to keep
> > track of types as they flow through the program. At any given step,
> > that's easy. When you make a mistake, you get an error.
> >
> > This is generally easy in a language that's got strong static typing; if
> > you have a mismatch, the compiler complains, and it's checked at every
> > reference.
> >
> > It's harder in a language like OpenSCAD that I would describe as
> > strongly typed (little automatic conversion) but dynamically typed (the
> > type isn't checked until you actually try to execute an operation). You
> > need naming and commenting conventions, and you need to pay attention.[*]
> >
> > If you're trying to detect an illegal operation... why? If the inputs
> > are the wrong type, then they are the wrong type and an error is
> > appropriate.
> >
> > I come up with two answers:
> >
> > * You want better or earlier errors. Rather than getting an error
> > from six levels down where the illegal operation finally happened,
> > you want an error at the top that says "Hey, bozo, you passed a
> > string for the length and it needs to be a number". Pretty much,
> > you want a type assertion; you would actually have been happier if
> > there had been a static typing mechanism so that the infrastructure
> > would do the check for you.
> > * You want to implement polymorphism, where two or more types are
> > allowed and yield different (though presumably similar) results.
> > For instance, rotate() is polymorphic - you can pass it either a
> > single number (interpreted as a rotation around the Z axis) or an
> > array of [x,y,z] rotations. Note that this isn't about detecting an
> > *illegal* operation - it's about determining the type of a value so
> > as to process it in an appropriate way.
> >
> > Exactly what problem are you trying to solve?
> >
> > ---
> >
> > [*]
> > <rant>
> > This is why I think the current tendency towards dynamic
> > typing and weak typing, as exemplified by languages like JavaScript,
> > is simply awful in the long term. (Python is better in simple
> > cases, but no better in complex cases.) Debugging large programs is
> > very difficult and programmers need every bit of automated
> > assistance that they can get. Dynamic typing and weak typing make
> > the language look simpler at the start, but cripple your ability to
> > statically analyze it and detect bugs before actually executing the
> > program.
> > </rant>
> >
> > _______________________________________________
> > OpenSCAD mailing list
>
> > Discuss@.openscad
>
> > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
>
>
>
>
> --
> Sent from: http://forum.openscad.org/
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>
A
adrianv
Sun, Aug 16, 2020 7:39 PM
These functions get called a LOT in the library, so speed is important. It
turns out that doing matrix operations is really fast in OpenSCAD, so
anything you can reframe as matrix operations will get faster. (For
example, I redid bezier calculations using matrices and it got an order of
magnitude faster.)
Our existing is_vector runs on a length 2000 vector in 0.03 ms. The version
you propose below takes 1.57 ms. So it's 52 times slower. So what, you
say? Well, this problem was brought to my attention because a certain short
example code produced thousands of warnings in the dev build from calls to
is_vector. This means that it's going to be slower by several seconds, so
it has a real time impact.
thehans wrote
These are still fairly simple functions to implement, and much clearer IMO
to explicitly check your types than trying a possibly undefined operation
to see if it sticks:
// check if a value is a list that contains only numbers (empty list
doesn't count)
function is_vecnum(v) = is_list(v) && len(v) && len([for(x=v)
if(!is_num(x)) 0]) == 0;
// check if a value is a list that is n elements long and all numbers:
function is_vecn(v,n) = is_list(v) && len(v)==n && len([for(x=v)
if(!is_num(x)) 0]) == 0;
// check if a value is a list of lists of numbers, all the same length
(again empty list, and list of empty lists don't count)
function is_matrix(m) = is_list(m) && len(m) && is_list(m[0]) &&
(let(n=len(m[0])) n && len([for(v=m) if(!is_vecn(v,n)) 0]) == 0);
On Sun, Aug 16, 2020 at 12:58 PM adrianv <
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid
operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance
that
an operation is illegal. There's no obvious way to determine if a
variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v
produces
an immediate warning for lists that aren't vectors. So if this is
staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of
the
same length).
--
Sent from: http://forum.openscad.org/
OpenSCAD mailing list
These functions get called a LOT in the library, so speed is important. It
turns out that doing matrix operations is really fast in OpenSCAD, so
anything you can reframe as matrix operations will get faster. (For
example, I redid bezier calculations using matrices and it got an order of
magnitude faster.)
Our existing is_vector runs on a length 2000 vector in 0.03 ms. The version
you propose below takes 1.57 ms. So it's 52 times slower. So what, you
say? Well, this problem was brought to my attention because a certain short
example code produced thousands of warnings in the dev build from calls to
is_vector. This means that it's going to be slower by several seconds, so
it has a real time impact.
thehans wrote
> These are still fairly simple functions to implement, and much clearer IMO
> to explicitly check your types than trying a possibly undefined operation
> to see if it sticks:
>
> // check if a value is a list that contains only numbers (empty list
> doesn't count)
> function is_vecnum(v) = is_list(v) && len(v) && len([for(x=v)
> if(!is_num(x)) 0]) == 0;
>
> // check if a value is a list that is n elements long and all numbers:
> function is_vecn(v,n) = is_list(v) && len(v)==n && len([for(x=v)
> if(!is_num(x)) 0]) == 0;
>
> // check if a value is a list of lists of numbers, all the same length
> (again empty list, and list of empty lists don't count)
> function is_matrix(m) = is_list(m) && len(m) && is_list(m[0]) &&
> (let(n=len(m[0])) n && len([for(v=m) if(!is_vecn(v,n)) 0]) == 0);
>
>
> On Sun, Aug 16, 2020 at 12:58 PM adrianv <
> avm4@
> > wrote:
>
>> I briefly tested the dev version from Aug 8 and found that the feature
>> discussed in another thread appears there, but not quite in the form
>> described in that thread.
>>
>> The description in the other thread was that illegal operations that
>> currently produce undef would generate a warning, but not until the undef
>> was used in an operation. It seems that in this version, invalid
>> operations
>> such as illegal matrix or vector products immediately produce a warning.
>>
>> The one issue this raises is that it's not very easy to tell in advance
>> that
>> an operation is illegal. There's no obvious way to determine if a
>> variable
>> is a vector, or a matrix, for example. We used to detect vectors with
>>
>> is_list(v) && is_num(0*(v*v))
>>
>> but this produces an immediate warning now, and in fact simply v*v
>> produces
>> an immediate warning for lists that aren't vectors. So if this is
>> staying
>> this way, then I'm wondering if we can have a mechanism for determining
>> validity of list data, like is_vector that is true only for a list of
>> numbers, or is_matrix that is true only for a matrix (list of lists of
>> the
>> same length).
>>
>>
>>
>> --
>> Sent from: http://forum.openscad.org/
>>
>> _______________________________________________
>> OpenSCAD mailing list
>>
> Discuss@.openscad
>> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>>
>
> _______________________________________________
> OpenSCAD mailing list
> Discuss@.openscad
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
--
Sent from: http://forum.openscad.org/
RP
Ronaldo Persiano
Sun, Aug 16, 2020 8:10 PM
Curiously, that dev still adds vectors of different dimensions without
complaints or undef returns! Is that legal? Isn't that idiosyncratic?
Em dom., 16 de ago. de 2020 às 18:58, adrianv avm4@cornell.edu escreveu:
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid
operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance
that
an operation is illegal. There's no obvious way to determine if a
variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v produces
an immediate warning for lists that aren't vectors. So if this is staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of the
same length).
Curiously, that dev still adds vectors of different dimensions without
complaints or undef returns! Is that legal? Isn't that idiosyncratic?
Em dom., 16 de ago. de 2020 às 18:58, adrianv <avm4@cornell.edu> escreveu:
> I briefly tested the dev version from Aug 8 and found that the feature
> discussed in another thread appears there, but not quite in the form
> described in that thread.
>
> The description in the other thread was that illegal operations that
> currently produce undef would generate a warning, but not until the undef
> was used in an operation. It seems that in this version, invalid
> operations
> such as illegal matrix or vector products immediately produce a warning.
>
> The one issue this raises is that it's not very easy to tell in advance
> that
> an operation is illegal. There's no obvious way to determine if a
> variable
> is a vector, or a matrix, for example. We used to detect vectors with
>
> is_list(v) && is_num(0*(v*v))
>
> but this produces an immediate warning now, and in fact simply v*v produces
> an immediate warning for lists that aren't vectors. So if this is staying
> this way, then I'm wondering if we can have a mechanism for determining
> validity of list data, like is_vector that is true only for a list of
> numbers, or is_matrix that is true only for a matrix (list of lists of the
> same length).
>
>
HL
Hans L
Sun, Aug 16, 2020 8:27 PM
Yes I noticed that while making my changes Ronaldo. Since it was not
already returning undef, I did not change behavior or add a warning, since
I figured someone was bound to complain of breaking changes in another
important feature that they use thousands of times in their code for
whatever reason.
Damned if you do and damned if you don't as always.
On Sun, Aug 16, 2020 at 3:11 PM Ronaldo Persiano rcmpersiano@gmail.com
wrote:
Curiously, that dev still adds vectors of different dimensions without
complaints or undef returns! Is that legal? Isn't that idiosyncratic?
Em dom., 16 de ago. de 2020 às 18:58, adrianv avm4@cornell.edu escreveu:
I briefly tested the dev version from Aug 8 and found that the feature
discussed in another thread appears there, but not quite in the form
described in that thread.
The description in the other thread was that illegal operations that
currently produce undef would generate a warning, but not until the undef
was used in an operation. It seems that in this version, invalid
operations
such as illegal matrix or vector products immediately produce a warning.
The one issue this raises is that it's not very easy to tell in advance
that
an operation is illegal. There's no obvious way to determine if a
variable
is a vector, or a matrix, for example. We used to detect vectors with
is_list(v) && is_num(0*(v*v))
but this produces an immediate warning now, and in fact simply v*v
produces
an immediate warning for lists that aren't vectors. So if this is
staying
this way, then I'm wondering if we can have a mechanism for determining
validity of list data, like is_vector that is true only for a list of
numbers, or is_matrix that is true only for a matrix (list of lists of the
same length).
Yes I noticed that while making my changes Ronaldo. Since it was not
already returning undef, I did not change behavior or add a warning, since
I figured someone was bound to complain of breaking changes in another
important feature that they use thousands of times in their code for
whatever reason.
Damned if you do and damned if you don't as always.
On Sun, Aug 16, 2020 at 3:11 PM Ronaldo Persiano <rcmpersiano@gmail.com>
wrote:
> Curiously, that dev still adds vectors of different dimensions without
> complaints or undef returns! Is that legal? Isn't that idiosyncratic?
>
> Em dom., 16 de ago. de 2020 às 18:58, adrianv <avm4@cornell.edu> escreveu:
>
>> I briefly tested the dev version from Aug 8 and found that the feature
>> discussed in another thread appears there, but not quite in the form
>> described in that thread.
>>
>> The description in the other thread was that illegal operations that
>> currently produce undef would generate a warning, but not until the undef
>> was used in an operation. It seems that in this version, invalid
>> operations
>> such as illegal matrix or vector products immediately produce a warning.
>>
>> The one issue this raises is that it's not very easy to tell in advance
>> that
>> an operation is illegal. There's no obvious way to determine if a
>> variable
>> is a vector, or a matrix, for example. We used to detect vectors with
>>
>> is_list(v) && is_num(0*(v*v))
>>
>> but this produces an immediate warning now, and in fact simply v*v
>> produces
>> an immediate warning for lists that aren't vectors. So if this is
>> staying
>> this way, then I'm wondering if we can have a mechanism for determining
>> validity of list data, like is_vector that is true only for a list of
>> numbers, or is_matrix that is true only for a matrix (list of lists of the
>> same length).
>>
>> _______________________________________________
> OpenSCAD mailing list
> Discuss@lists.openscad.org
> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
>