JavaScript. Array "extras" in detail.

  • Thread starter Dmitry A. Soshnikov
  • Start date
D

Dr J R Stockton

D

Dmitry A. Soshnikov

In comp.lang.javascript message<[email protected]
september.org>, Wed, 23 Feb 2011 18:46:05, Dmitry A. Soshnikov


Interesting.

Reminds me of<http://en.wikipedia.org/wiki/Jensen's_Device>.

Yeah, actually roots of this idea (of the parametrized list handling)
went to also to math. There, mathematician are used to operate with the
concept of _a sum_ (regardless the sum of _what_ exactly). They have a
special higher-order function (HOF) of summation with the known sign --
∑ (∑).

My example with a generic sum is exactly a direct reflection of this
math concept. And as a consequence, higher-order functions can be
applied for any manipulation on the sequences/lists/arrays. That's the
reason they are available in all functional programming langauges
(including JS which supports this style of programming, since all
functions here are "first-class" and can be passed to HOFs).
Link added to<http://www.merlyn.demon.co.uk/js-other.htm>, which will
increase my chances of finding it again.

Yeah, thanks.

Dmitry.
 
M

Michael Haufe (\TNO\)

On Feb 25, 5:30 am, "Dmitry A. Soshnikov" <[email protected]>
wrote:
[...]
My example with a generic sum is exactly a direct reflection of this
math concept. And as a consequence, higher-order functions can be
applied for any manipulation on the sequences/lists/arrays. That's the
reason they are available in all functional programming langauges
(including JS which supports this style of programming, since all
functions here are "first-class" and can be passed to HOFs).
[...]

It is unfortunate that JavaScript couldn't be a bit less clunky in the
functional style:

["1","2","3"].map(parseInt); //[1, NaN, NaN]
 
L

Lasse Reichstein Nielsen

Michael Haufe (\"TNO\") said:
It is unfortunate that JavaScript couldn't be a bit less clunky in the
functional style:

["1","2","3"].map(parseInt); //[1, NaN, NaN]

Don't blame your tools. The map function passes three arguments to
the function, and parseInt expects two. What you want is probably:
["1","2","3"].map(function(x) { return parseInt(x, 10); });

/L
 
M

Michael Haufe (\TNO\)

Michael Haufe (\"TNO\") said:
It is unfortunate that JavaScript couldn't be a bit less clunky in the
functional style:
["1","2","3"].map(parseInt); //[1, NaN, NaN]

Don't blame your tools. The map function passes three arguments to
the function, and parseInt expects two. What you want is probably:
 ["1","2","3"].map(function(x) { return parseInt(x, 10); });

Alternatively for this example: ["1","2","3"].map(Number);

The point is that it is often the case that solutions like yours are
necessary in these types of methods which I find clunky.
 
R

Ry Nohryb

It is unfortunate that JavaScript couldn't be a bit less clunky in the
functional style:
["1","2","3"].map(parseInt); //[1, NaN, NaN]
Don't blame your tools. The map function passes three arguments to
the function, and parseInt expects two. What you want is probably:
 ["1","2","3"].map(function(x) { return parseInt(x, 10); });

Alternatively for this example: ["1","2","3"].map(Number);

The point is that it is often the case that solutions like yours are
necessary in these types of methods which I find clunky.

Not clunky, no, if you put garbage in you get garbage out.
 
M

Michael Haufe (\TNO\)

Not clunky, no, if you put garbage in you get garbage out.


If a construct is not intuitive and requires extra boilerplate to make
it work, it can be considered clunky. Having to use "function()
{ return ... }" or similar to massage parameters is not something to
smile at. \be's shorter function syntax will help a bit in this area
"#(x) { x * x }", but even this only dulls the pain of having to do it
in the first place. Compare code similar to the above with other
functional languages such as Miranda, Haskell, OCaml, SML, F#, popular
Lisp languages and others, and you'll see what I mean. These array
"extras" and "Array generics" go quite far to help enable a more
functional programming style in JS but with the choices made in their
signatures it has made it painful to perform one of the most important
important aspects of developing in the functional style : composition.
irt being non-intuitive, if you've used one or more of the above
languages little things like this are probably going to bite you more
than once. I'm not trying to rally against these new array methods,
but simply showing that there is another side of the coin.
 
L

Lasse Reichstein Nielsen

Michael Haufe (\"TNO\") said:
If a construct is not intuitive and requires extra boilerplate to make
it work, it can be considered clunky.

What part of the construct is it that is not intuitive here?

My guess is the fact that map actually passes three arguments, not
just one, which differs from, e.g., Lisp's map-car or Scheme's map.
But that's actually very much a Javascript-like feature.
Having to use "function()
{ return ... }" or similar to massage parameters is not something to
smile at. \be's shorter function syntax will help a bit in this area
"#(x) { x * x }", but even this only dulls the pain of having to do it
in the first place. Compare code similar to the above with other
functional languages such as Miranda, Haskell, OCaml, SML, F#, popular
Lisp languages and others, and you'll see what I mean.

Notice that neither of those languages allow you to call a function with
fewer or more arguments than what you expect. That means that you won't
even typecheck (for the statically typed langauges) if your map passed
three arguments and you function expected two.

The same function would fail in Lisp and Scheme, if map passed three
arguments.

If you had a map function passing three arguments in SML, you would
still have to write
map myList (fn (x,i,l) => myUnaryFunction(x))
to use it with an unary function.

Javascript is unique among the functional languages you mention in that
you can pass too few or too many arguments to a function. That's why they
allowed themselves to let map pass three arguments instead of just one.

If you have a function expecting just one argument, then it still works.

What you did here was to pass a function that expected two arguments
(but would work with only one) and you got predictably incorrect
results to a function calling it with three arguments, where the second
did not work as a second argument for the first function.
These array "extras" and "Array generics" go quite far to help
enable a more functional programming style in JS but with the
choices made in their signatures it has made it painful to perform
one of the most important important aspects of developing in the
functional style : composition.

If your functions don't allow a variable number of arguments, then
there is no problem - it will ignore any surplus arguments.

The way Javascript is designed, that's sadly not something you can
rely on.
irt being non-intuitive, if you've used one or more of the above
languages little things like this are probably going to bite you more
than once. I'm not trying to rally against these new array methods,
but simply showing that there is another side of the coin.

The Javascript array functions differ from similar list-functions in
other languages. That's both good and bad - they actually fit
Javascript well, since the language allows optional arguments so
easily, but they bite you when you combine two different ways of being
optional (passing potentially unnecessary extra arguments to a
function expecting optional arguments, where the user thinks just of
the one-argument case).

/L 'Never call parseInt with only one argument'.
 
M

Michael Haufe (\TNO\)

What part of the construct is it that is not intuitive here?

My guess is the fact that map actually passes three arguments, not
just one, which differs from, e.g., Lisp's map-car or Scheme's map.
Yes

But that's actually very much a Javascript-like feature.


Notice that neither of those languages allow you to call a function with
fewer or more arguments than what you expect.

Fewer is allowed, but not more. (currying) Unless you are referring to
Lisp/Scheme only by saying "neither"?
That means that you won't
even typecheck (for the statically typed langauges) if your map passed
three arguments and you function expected two.

The same function would fail in Lisp and Scheme, if map passed three
arguments.

If you had a map function passing three arguments in SML, you would
still have to write
   map myList (fn (x,i,l) => myUnaryFunction(x))
to use it with an unary function.

Of course since mutability is avoided, the third argument would have
little reason to exist. For the second it's a little meaningless since
the use case are hard to find. In fact I don't think there is even an
(indexof foo) style function in the standard lib.
Javascript is unique among the functional languages you mention in that
you can pass too few or too many arguments to a function.

Not quite... <http://mlton.org/Fold>

Unless you want to compare arbitrary size tuples to JS arguments.
That's why they
allowed themselves to let map pass three arguments instead of just one.

If you have a function expecting just one argument, then it still works.

What you did here was to pass a function that expected two arguments
(but would work with only one) and you got predictably incorrect
results to a function calling it with three arguments, where the second
did not work as a second argument for the first function.

Yes. It looks like a similar discussion as this one is going on here:
<http://www.wirfs-brock.com/allen/posts/166>.
Also here <http://whereswalden.com/2011/02/26/the-proper-way-to-call-
parseint-tldr-parseintstr-radix/>
If your functions don't allow a variable number of arguments, then
there is no problem - it will ignore any surplus arguments.

The way Javascript is designed, that's sadly not something you can
rely on.

The identity crises strikes again.
The Javascript array functions differ from similar list-functions in
other languages. That's both good and bad - they actually fit
Javascript well, since the language allows optional arguments so
easily, but they bite you when you combine two different ways of being
optional (passing potentially unnecessary extra arguments to a
function expecting optional arguments, where the user thinks just of
the one-argument case).

In one of the links above there was a suggestion of adding
Function.prototype.only(...) as a solution. Though I find that just as
bad since in the case of parseInt you still have to know there was a
problem in the first place requiring this type of solution.
 
T

Thomas 'PointedEars' Lahn

Lasse said:
Michael Haufe (\"TNO\") said:
It is unfortunate that JavaScript couldn't be a bit less clunky in the
functional style:

["1","2","3"].map(parseInt); //[1, NaN, NaN]

Don't blame your tools. The map function passes three arguments to
the function, and parseInt expects two. What you want is probably:
["1","2","3"].map(function(x) { return parseInt(x, 10); });

Or

/* [1, 2, 3] */
[,, "1","2","3"].map(parseInt).slice(2);

:)


PointedEars
 
V

VK

A new article written specially for Opera software:

JavaScript. Array "extras" in detail.

http://dev.opera.com/articles/view/javascript-array-extras-in-detail/

An interesting outcome for forEach from some Javascript features:
1) JavaScript Array allowed to be sparse
2) An array element can be
a) initialized and value assigned such as arr[1] for arr=[1,2,3]
b) initialized but no value assigned (Empty by VBA) such as arr[10]
for arr=new Array(100)
c) not initialized (undefined) such as arr[10] for arr=[1,2,3]
but Javascript takes a) and b) as the same case
3) Javascript allows to assign undefined values so it allows to
produce epistemologically mysterious variables with assigned value
"variable has not been assigned a value".

All this together makes the fact that in Javascript array an elision
and undefined mean different things and act differently. In the
particular, in array [1,,2] forEach method will see only two elements
to deal with and in array [1,undefined,3] forEach will see three
elements.
Respectively in Javascript array [1,2,3]
delete arr[0]; // 2 members forEach
and
arr[0] = undefined; // still 3 members forEach
are different things with different results.

All this may seem very obvious and logical for people who really
understand Javascript. For others (like myself) mentioning that in
your article could be useful.
 
V

VK

Depends on what you mean by "initialized". Going from the examples you
give, it looks like you call an element "initialized" when its index is
less than the array's length.

Right. More formally: an element that is located within the currently
defined array borders. This is what reported as Empty (as opposed to
Undefined or Null) in Visual Basic and VBA.
That may be significant in other
languages, but it isn't in JavaScript. The .length property, whether set
explicitly via 'arr.length' and 'new Array(n)', or implicitly by
assigning to arr[largerNumber], has nothing to do with creating or
initializing any indices (properties) in the array.

It is true for any language. I am not aware of a language that on say
its analogy for
new Array(4294967294)
would loop 4,294,967,294 times making some preliminary
initializations :)
Case in point: your example in b), new Array(100), does not create all
the elements from 0 to 99, it only sets the .length property of the
array. That's all. arr[10] evaluates to undefined for the same reason
that arr["ten"] would: the property doesn't exist.

Once I had a discussion on it in this group. It all depends on the
level of abstraction one chooses. One can look at Array as an array.
One can look at it as a regular object with a fancy auto-updating
property added. One can say that
var arr = new Array(100);
creates an array with 100 elements in it which are all initialized but
not assigned.
The other can say that that creates a generic Object, sets its
prototype to Array prototype and sets its auto-updating property
"length" to 100.
Both are right but IMO the second one doesn't exactly speak about
Javascript: she is one level lower than needed.
It's just like with types in Javascript. Javascript is a dynamically
typed ("loosely typed" as haters call it) language. That means that
any variable at some given moments has certain type but this type can
be dynamically changed by assigning value of another type. That is
different from VBScript which is typeless or more exactly any variable
is of the same universal type Variant.
Yet an XPCOM programmer will say that in Gecko's JavaScript all
variables are of the same universal type, just like in VBScript. She
will be right as well but from her level which is again one level
lower than needed to talk about a programming language as about a
programming language per se.
An index (property) will only exist if you create it by assigning
something to it. This can be done by assigning to arr[n], or by giving
it a value in an array literal.
(There's also the alternative Array constructor form with more than one
argument, but the less said about that the better. I suppose
Object.defineProperty() might also be used, but I can't test that at the
moment).

None of this has much to do with the length of the array, except that it
will be automatically adjusted behind the scenes when you assign to an
index greater or equal to the value of the .length property.

So you prefer to talk from behind the scene and I do on the scene :)
It may help to keep the difference between indices (=properties) and
values in mind. Remember, undefined is a value, too. In your last
example, something like 'arr[3] = undefined' would actually increase the
length of the array instead of removing an element.

As I said, my problems with
var x = undefined;
and the like are epistemological and not technical. I do understand
the programmatic outcome yet I refuse to take as sensual "variable
with assigned value 'variable has not been assigned a value'".
The elision syntax is a special case. IMHO, elisions are unintuitive and
generally not very useful. I think it's best to avoid them altogether.

They are definitely a very rare bird, just like named for{} loops. Yet
it might be a situation when one needs to create (automatically server-
side for instance) sparse array with gaps here and there and to make
sure that forEach will work only with assigned array members. Then she
needs to know that elisions will do the work
[0,1,2,3,,,6,,8]
and undefined will not
[0,1,2,3,undefined,undefined,6,undefined,8]
 
J

John G Harris

On Tue, 29 Mar 2011 at 05:52:27, in comp.lang.javascript, VK wrote:

I am not aware of a language that on say
its analogy for
new Array(4294967294)
would loop 4,294,967,294 times making some preliminary
initializations :)
<snip>

Try C++.

John
 
V

VK

On Tue, 29 Mar 2011 at 05:52:27, in comp.lang.javascript, VK wrote:



  <snip>

Try C++.

Uhm... I am not a C++ memory management expert but this article says:
http://www.cplusplus.com/doc/tutorial/arrays/
"When declaring a regular array of local scope (within a function, for
example), if we do not specify otherwise, its elements will not be
initialized to any value by default, so their content will be
undetermined until we store some value in them."

From the point of view of the sanity for say
long double arr[4294967294];
the maximum I would expect is an early warning from the compiler that
the current RAM size doesn't allow to initialize all 4,294,967,294
elements of the given type. If the engine indeed loops 4,294,967,294
times for the above statement then it is not wonder at all that C++ers
escaping to Javascript left and right :)
 
L

Lasse Reichstein Nielsen

VK said:
Uhm... I am not a C++ memory management expert but this article says:
http://www.cplusplus.com/doc/tutorial/arrays/
"When declaring a regular array of local scope (within a function, for
example), if we do not specify otherwise, its elements will not be
initialized to any value by default, so their content will be
undetermined until we store some value in them."

That might hold for local arrays of primitive values, but try doing
it for a type with a default constructor.
From the point of view of the sanity for say
long double arr[4294967294];
the maximum I would expect is an early warning from the compiler that
the current RAM size doesn't allow to initialize all 4,294,967,294
elements of the given type.

Not if compiling for 64-bit (although you might want to make the constant
either unsigned or long). The compiler won't know the size of memory on
the computer where it will be run, so at best it can give a warning.

But why guess. I just happen to have a C++ compiler lying around :)

-- bigarray.cc --
class ByteBox {
public:
ByteBox() : byte(0) { }
unsigned char byte;
};

int main(int argc, char* argv) {
ByteBox big_array[4294967294u];
return big_array[0].byte;
}
--

It compiles happily as 64-bit code. It won't run (the stack's not big enough
to stack-allocate that, that would require 16Gb stack in this case).
Disassembling the code, you can see that it actually would have tried to
initialize the entire array.

Now try non-stack allocation, which arguably better matches the JS example:

-- bigarray2.cc --
class ByteBox {
public:
ByteBox() : byte(0) { }
unsigned char byte;
};

int main(int argc, char* argv[]) {
ByteBox* big_array = new ByteBox[4294967294u];
unsigned char res = big_array[0].byte;
delete[] big_array;
return res;
}
--

Try to run that, and it will spend a gazillion years initializing
memory, while swapping a lot. But it will run.
If the engine indeed loops 4,294,967,294 times for the above
statement then it is not wonder at all that C++ers escaping to
Javascript left and right :)

In C++, you get what you ask for, no hands held, no holds barred.
Rope enough to both hang yourself and still have wiggle-room enough to
shoot yourself in the foot too.

/L
 
J

John G Harris

In C++, you get what you ask for, no hands held, no holds barred.
Rope enough to both hang yourself and still have wiggle-room enough to
shoot yourself in the foot too.

True. C++ is not for bad programmers. They prefer to write bad libraries
for web pages that stop people buying things.

John
 
V

VK

I see your "ideal programming environment" to be a kind of a pigheaded
yet highly dutiful solder who does everything literally as being said:
no questions asked, no assumptions made, no complains expressed. :)
In this case assembly language leaves far behind not only C++ but C as
well. And the whole history of the programming becomes the story of
lost freedom and going away from the ideal :)
True. C++ is not for bad programmers. They prefer to write bad libraries
for web pages that stop people buying things.

Oh yes. This is why only bad Javascript libraries exist while any C++
library is a superior quality product by the very nature of the
language. :)) The only real difference is that Javascript source code
is always available for viewing and analyzing when C++ source is
usually mercifully hidden from our eyes by the compiler. I am saying
"mercifully" because otherwise jQuery soon become for you a sample of
programming culture and professionalism :)
 
J

John G Harris

On Mon, 18 Apr 2011 at 12:13:35, in comp.lang.javascript, VK wrote:

Oh yes. This is why only bad Javascript libraries exist while any C++
library is a superior quality product by the very nature of the
language. :)) The only real difference is that Javascript source code
is always available for viewing and analyzing when C++ source is
usually mercifully hidden from our eyes by the compiler. I am saying
"mercifully" because otherwise jQuery soon become for you a sample of
programming culture and professionalism :)

On the contrary. A C++ library's header files have to be visible to the
user, so revealing much of the design. That's why so many people said
Yuk! about Microsoft's MFC windows library. The difference from JQuery
is that Microsoft made sure their compilers stayed compatible with MFC.
And they didn't boast about it being good enough, and scream at anyone
who didn't like it.

John
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top