Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Javascript > Creating true copies (of objects) in JS (possible?)

Reply
Thread Tools

Creating true copies (of objects) in JS (possible?)

 
 
Svend Ezaki Tofte (DIKU)
Guest
Posts: n/a
 
      11-03-2003
On Mon, 3 Nov 2003, Dom Leonard wrote:

> From code at the URL quotes below can see how you (Svend) solved the
> "problem" incurred by not having separate copies of accumulated
> arguments, but would point out that the inner function
>
> return function (){
> // arguments are params, so closure bussiness is avoided.
> return accumulator(arguments,sa.slice(0),n);
> }
>
> does not prevent nested closures from being created. It does defeat
> reference to outer closure variables and parameter names by using "sa"
> and "n" as formal parameter names of the "accumulator" function called.


I know closures are not prevented. But accumulation of arguments are
prevented. Which was the goal. Closures prevented this, argument passing
solved this.

> FWIW, the alternative of copying both previous and freshly supplied
> arguments to a new accumulation array obviates the need to consider
> reinstatement of closure variables, and using the (make) curry function
> itself to accumulate old and new argument arrays can avoid the creation
> of deeply nested closures.


I don't understand this?

> I would tend to put that more strongly myself: remove the "this." in
> front of "add". Not just to run within an evaluator, but because it
> actually requires that "add" be a method of its "this" value. This is
> false in javascript if "add" were defined as a nested function and
> called without object qualification. For those unfamiliar with the
> outcome, "this" within "add" would take on the value of the global
> object in such a case.


Reminds me of a paper Waldemar wrote on JS 2.0. That if you understood
that, you've been reading too many language specs [1]

Everytime I feel like I know JS, I see more of the language, and just how
truly twisted it really is... !

Regards,
Svend

[1] http://www.mozilla.org/js/language/evolvingJS.pdf (page 5)
 
Reply With Quote
 
 
 
 
Dom Leonard
Guest
Posts: n/a
 
      11-04-2003
Svend Ezaki Tofte (DIKU) wrote:
> On Mon, 3 Nov 2003, Dom Leonard wrote:


>
>>FWIW, the alternative of copying both previous and freshly supplied
>>arguments to a new accumulation array obviates the need to consider
>>reinstatement of closure variables, and using the (make) curry function
>>itself to accumulate old and new argument arrays can avoid the creation
>>of deeply nested closures.

>
>
> I don't understand this?
>


Now we both have a problem, I'm confused too

Looking at your code more closely I am left wondering if the "need to
reset" more properly belongs to a previous version of the function.
Rewritten to take out the outer variables which are hidden by parameter
names in nested function "accummulator", I got:


function curry(func,args,space)
{
function accumulator(moreArgs, cumeArgs, n)
{

// copy existing and number of remaining

var saPrev = cumeArgs.slice(0); // shallow copy
var nPrev = n; // to reset

// extend parameter array

for(var i=0;i<moreArgs.length;i++,n--) {
cumeArgs[cumeArgs.length] = moreArgs[i];
}

// if enough to execute, execute function
// and restore arguments (which are held in closure)

if ((n-moreArgs.length)<=0) {
var res = func.apply(space,cumeArgs);
// reset vars, so curried function can be applied to new params.
// cumeArgs = saPrev;
// n = nPrev;
return res;
}
else
{
return function (){
// arguments are params, so closure bussiness is avoided.
return accumulator(
arguments,
cumeArgs.slice(0), // shallow copy
n // updated arguments to come
);
}
}
}

return accumulator(
[],
Array.prototype.slice.apply(args), // shallow copy
func.length - args.length //arguments still to come
);
}

----

Curried functions appears to copy existing, accumulated arguments into a
formal parameter array passed to "accumulator" executing and creating a
closure one level down from the call. Effectively this parameter array
is the "new accumulation" array I saw as logically required.

What I now can't see, or find examples for, is why there would be a need
to restore the parameter array when a nested call to "accumulator"
executes "func" - should not the copy of accumulated arguments in its
2nd formal parameter go out of existence anyway?


Regards,

Dom

 
Reply With Quote
 
 
 
 
Svend Ezaki Tofte (DIKU)
Guest
Posts: n/a
 
      11-04-2003
On Wed, 5 Nov 2003, Dom Leonard wrote:

> Looking at your code more closely I am left wondering if the "need to
> reset" more properly belongs to a previous version of the function.
> Rewritten to take out the outer variables which are hidden by parameter
> names in nested function "accummulator", I got:
>
> [lotsa code snipped]


Mmmm, I like that even more. Of course, totally parametizing the arguments
eliminates the whole closure bussiness even more. I'll have to rip that
for my script

> What I now can't see, or find examples for, is why there would be a need
> to restore the parameter array when a nested call to "accumulator"
> executes "func" - should not the copy of accumulated arguments in its
> 2nd formal parameter go out of existence anyway?


But, and this is without testing your code (I see you've commented out the
reset part I had in there), the function will remember the passed
arguments.
Or what? I'm not sure why it should fall out of scope, and just be
forgotten?
The function returned by curry, accumulator, will keep accumulating
arguments,
and will never remove any.

Call it again and again, and it'll stick the new variables onto the end of
all the old ones.

Or am I misunderstanding your point?

Regards,
Svend


 
Reply With Quote
 
Richard Cornford
Guest
Posts: n/a
 
      11-05-2003
"Dom Leonard" <(E-Mail Removed)> wrote in message
news:S_Opb.536$(E-Mail Removed)...
<snip>
>Looking at your code more closely I am left wondering if the
>"need to reset" more properly belongs to a previous version
>of the function. Rewritten to take out the outer variables
>which are hidden by parameter names in nested function
>"accummulator", I got:
>
> function curry(func,args,space)

<snip>
> }
>
> ----
>Curried functions appears to copy existing, accumulated arguments
>into a formal parameter array passed to "accumulator" executing
>and creating a closure one level down from the call. Effectively
>this parameter array is the "new accumulation" array I saw as
>logically required.


>What I now can't see, or find examples for, is why there would be
>a need to restore the parameter array when a nested call to
>"accumulator" executes "func" - should not the copy of accumulated
>arguments in its 2nd formal parameter go out of existence anyway?


This is an intriguing problem and I thought that I would have a go at
it. I didn't like the idea of accumulating the parameters in an array so
I traded that off against an increased number of function objects, but
along the way I gained the ability to pass some, or all, of the
arguments to the original call to the curry function with the function
that is to be executed.

This is my version:-

<html>
<head>
<title></title>
<script type="text/javascript">

function curry(f){
var obj = this; //global, unless this function
//is assigned as a method.
arguments.callee.depth = 0;
function getArgFunc(args, af){
function argFunc(x){
var retVal;
var self = arguments.callee;
function getArgs(){
var ar = self.getArgs();
ar[ar.length] = x;
return ar;
}
if(self.depth == f.length){
retVal = f.apply(obj, getArgs());
}else{
retVal = getArgFunc(arguments, getArgs);
}
return retVal;
}
argFunc.depth = args.callee.depth+1;
argFunc.getArgs = af;
for(var c = 1;c < args.length;c++){
argFunc = argFunc(args[c]);
if((typeof argFunc != 'function')||(!argFunc.getArgs)){
break;
}
}
args = null;
return argFunc;
}
return getArgFunc(arguments, function(){return [];});
}

</script>
</head>
<body>

<script type="text/javascript">

function add(x,y,z,a) {return x+','+y+','+z+','+a;}

var cadd = curry(add);

var cadd1 = cadd(1);
var cadd2 = cadd(2);
var cadd13 = cadd1(3);
var cadd15 = cadd1(5);
var cadd27 = cadd2(7);

var cadd13A = cadd13(10);
var cadd159 = cadd15(9);
var cadd27D = cadd27(13)
document.write('cadd13A(6) = '+cadd13A(6)+' :<br>');
document.write('cadd159( = '+cadd159(+' :<br>');
document.write('cadd27D(5) = '+cadd27D(5)+' :<br>');

document.write('cadd13A(1) = '+cadd13A(1)+' :<br>');
var cadd134 = cadd13(4);
document.write('cadd134(5) = '+cadd134(5)+' :<br>');

var cadd12 = cadd(1,2);
var cadd123 = cadd12(3);
document.write('cadd123(5) = '+cadd123(5)+' :<br>');
document.write('cadd12(6,7) = '+cadd12(6,7)+' :<br>');
document.write('curry(add,4,5,6,7) = '+curry(add,4,5,6,7)+' :<br>');
document.write('cadd2(4,6, = '+cadd2(4,6,+' :<br>');
document.write('cadd27(100,120) = '+cadd27(100,120)+' :<br>');
var cadd98 = curry(add,9,
document.write('cadd98(7,6) = '+cadd98(7,6)+' :<br>');
</script>
</body>
</html>

Richard.


 
Reply With Quote
 
Dom Leonard
Guest
Posts: n/a
 
      11-05-2003
Svend Ezaki Tofte (DIKU) wrote:
> On Wed, 5 Nov 2003, Dom Leonard wrote:
>

<snip>
>
>
> Of course, totally parametizing the arguments
> eliminates the whole closure bussiness even more. I'll have to rip that
> for my script
>


Great, progress is in progress


>
>>What I now can't see, or find examples for, is why there would be a need
>>to restore the parameter array when a nested call to "accumulator"
>>executes "func" - should not the copy of accumulated arguments in its
>>2nd formal parameter go out of existence anyway?

>
>
> But, and this is without testing your code (I see you've commented out the
> reset part I had in there), the function will remember the passed
> arguments.


Okay, I've got it now (well...)

A bug in the unmodified version was that calling "curry" shallow copied
arguments into local variable "sa", and then passed that array to
"accumulator" by *reference*.

The first call to "accumulator" now operated on an array held in the
outermost closure, and interferred with re-use of the outermost curried
function.

Copying existing arguments into a parameter array in the outermost
closure peformed a neccessary dynamic copy, made it consistent with the
same operation present in the closure created by "accumulator", and
hence removes the need to "reset" argument arrays (which probably was
not a perfect solution, but at this point who cares



> Or what? I'm not sure why it should fall out of scope, and just be
> forgotten?


If "accumulator" executes "func" it does not return a local nested
function, and so does not create a closure. On exit its formal
paraameters will not be held in scope anywhere. If the calling function
does not hold a variable set to the parameter array copied in call, it's
gone.

Hopefully you will now find creaation of nPrev and saPrev variables, and
the business of resetting parameter arrays unnecessary.

Regards,

Dom

 
Reply With Quote
 
Dom Leonard
Guest
Posts: n/a
 
      11-05-2003
Snap, we both posted at same time

It is certainly intriguing code usage, and I'll have a look at your
version when I get off line. For your interest and comparison, the
version I posted some monts ago went:

----

function curry(func,oldArgs)
{ function accumulator()
{ var newArgs = arguments;
if(newArgs.length ==0)
return accumulator; // no change
if(oldArgs)
{ newArgs=new Array();
newArgs.push.apply(newArgs,oldArgs);
newArgs.push.apply(newArgs,arguments);
}
return (newArgs.length >= func.length) ?
func.apply(this,newArgs) :
curry(func,newArgs);
}
return func.length ? accumulator : func;
}


----

It contains some "belts and braces" for boundary conditions that may not
be needed, and possibly could use Array.prototype.splice - I just didn't
see where at the time, and Svend's version is coming along too

Cheers,

Dom









 
Reply With Quote
 
Richard Cornford
Guest
Posts: n/a
 
      11-05-2003
"Dom Leonard" <(E-Mail Removed)> wrote in message
news:l6%pb.116$(E-Mail Removed)...
>Snap, we both posted at same time


>It is certainly intriguing code usage, and I'll have a look
>at your version when I get off line. For your interest and
>comparison, the version I posted some monts ago went:
>
> ----
>
> function curry(func,oldArgs)
> { function accumulator()
> { var newArgs = arguments;
> if(newArgs.length ==0)
> return accumulator; // no change


That possibility didn't occur to me, but it does need to be covered.

> if(oldArgs)
> { newArgs=new Array();
> newArgs.push.apply(newArgs,oldArgs);
> newArgs.push.apply(newArgs,arguments);
> }
> return (newArgs.length >= func.length) ?
> func.apply(this,newArgs) :
> curry(func,newArgs);
> }
> return func.length ? accumulator : func;


And thinking about the possibility that the parameter adding function
may be called without an argument it also occurred to me that the
function (func) itself may not require any parameters, which you have
covered here. However, if the function needs no parameters (func.length
== 0) shouldn't the value returned at this point be the result of
calling that function? As my version allows the initial call to curry to
be supplied with all of the required parameters and will execute the
function if they are all present I went with executing the function in
curry if func.length == 0.

> }


> ----
>
>It contains some "belts and braces" for boundary conditions
>that may not be needed, and possibly could use
>Array.prototype.splice - I just didn't see where at the time,
>and Svend's version is coming along too


Being reminded of the belts and braces was useful, here are my
corrections for those (at least the ones I have noticed so far):-

function curry(f){
var retVal,obj = this; //global, unless this function
//is assigned as a method.
arguments.callee.depth = 0;
function getArgFunc(args, af){
function argFunc(x){
var retVal;
var argsLen = arguments.length;
var self = arguments.callee;
function getArgs(){
var ar = self.getArgs();
if(argsLen){ar[ar.length] = x;}
return ar;
}
if(self.depth >= f.length){
retVal = f.apply(obj, getArgs());
}else if(!argsLen){
retVal = self;
}else{
retVal = getArgFunc(arguments, getArgs);
}
return retVal;
}
argFunc.depth = args.callee.depth+1;
argFunc.getArgs = af;
for(var c = 1;c < args.length;c++){
argFunc = argFunc(args[c]);
if((typeof argFunc != 'function')||(!argFunc.getArgs)){
break;
}
}
af = (args = null);
return argFunc;
}
retVal = getArgFunc(arguments, function(){return [];});
return (f.length)?retVal:retVal();
}

Richard.


 
Reply With Quote
 
Lasse Reichstein Nielsen
Guest
Posts: n/a
 
      11-05-2003
"Svend Ezaki Tofte (DIKU)" <(E-Mail Removed)> writes:

> I'll have to test this code out. From the looks of it, I don't see how it
> would work. Wouldn't fx. cadd13 always return 14 now? Because it's
> newsargs array would contain 1, 3, and 10?


No, cadd13's internal array is [1,3]. Each function gets a new array, so
they don't mess up each other's.

It can be shortened using some of the tricks you have used in the
later discussion, and made to not require arrays, but also accept
arguments objects:

function curry(func,args) { // args is optional argument.
if (!args) {args = [];}
return function curried() {
if (arguments.length == 0) {return curried;} //optimization, !essential
var newargs = Array.prototype.slice.call(args,0); // array or arguments
Array.prototype.push.apply(newargs,arguments); // someone was smart!
if (newargs.length >= func.length) {
return func.apply(this,newargs);
} else {
return curry(func,newargs);
}
}
}

It doesn't preserve the object that the original function is a method
of, since it can't know it. Should it remember some object that a
partially applied function is assigned to? I don't think so. As it is
now, you can assign a partially applied curried function as a method
and have it work correctly when you give it the remaining arguments
directly:

var o1 = new Object();
o1.foo = -1000000;
var o2 = new Object();
o2.foo = 42;
o1.cadd = curry(function(x,y,z){return x+y+z+this.foo});
o2.cadd13 = o1.cadd(1,3);
alert(o2.cadd13(-9));

The most meaningfull result of this code is that the this.foo
referst to o2, since cadd13 is a method of o2 when it is called.
If it had remembered that cadd was a method of o1, then it would
give a different, and IMO incorrect result.



It is easy for functions to curry themselves:

function add(x,y,z) {
if (arguments.length < add.length) {
return curry(add,arguments);
}
return x+y+z;
}


> > That still only allows you to use the curried function sequentially. You
> > can't give it one argument, and then call the resulting function twice
> > on different second arguments.

>
> What do you mean?


That I hadn't testet the code. It works fine.

> Does this refer to the above also?


Yep.


/L
--
Lasse Reichstein Nielsen - http://www.velocityreviews.com/forums/(E-Mail Removed)
DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
'Faith without judgement merely degrades the spirit divine.'
 
Reply With Quote
 
Svend Ezaki Tofte (DIKU)
Guest
Posts: n/a
 
      11-05-2003

Everyone who responded to this thread,

I feel really bad for having started a thread, and having generated so
many good responses, and then not even having time to fully look at the
answers (that isn't the sole justification of this thread of course!).

I find it interesting though, how an issue like currying can be applied in
a language like JS. Perhaps when winter time comes, I'll be posting
again... with more questions

Regards,
Svend

(I've got to go read some psych bullshit now, if you'll excuse me ... )
 
Reply With Quote
 
Dom Leonard
Guest
Posts: n/a
 
      11-07-2003
Richard Cornford wrote:

<snip>


> ... it also occurred to me that the
> function (func) itself may not require any parameters, which you have
> covered here. However, if the function needs no parameters (func.length
> == 0) shouldn't the value returned at this point be the result of
> calling that function?


I considered the effect some months ago, but ultimately thought the
"desirable" behaviour may be unknowable. A curried funtion, pursuant to
the currying operation in javascript, has a function length property of
zero. If on account of that, currying a curried function returns the
curried function, then "curry" on a curried function results in the
identity transform.

In the version I propsosed, the same identity transform results from
calling a curried function with no parameters - it returned itself.

The question remaining is what should the curry operation, at the top
level, do with a function that has a zero length property? Options are
to return the function (my example), calling the function (your example)
or throw an exception that curry was attempted on a function expecting
no arguments.

Whilst I tend to the last option, it's not hard and fast. In an
reasonable program I would not expect the problem to arise, but am happy
to defer to any theoretician with a rule for what should happen.



kind regards,

Dom

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[False,True] and [True,True] --> [True, True]????? bdb112 Python 45 04-29-2009 02:35 AM
"0 in [True,False]" returns True Pierre Quentel Python 59 12-16-2005 01:47 PM
TurboGears /.-ed, >new == True< or >new == "True"< Andy Leszczynski Python 4 10-13-2005 06:56 AM
C and C++ are interoperable languages? True or Not True? Chip C++ 6 01-08-2005 11:10 PM
Does true ^ true return false? Siemel Naran C++ 19 06-18-2004 11:06 AM



Advertisments