Feature Copy constructor for ASH records (and possibly arrays/maps as well?)

heeheehee

Developer
Staff member
Seems like some scripters nowadays are running into "call-by-reference" issues (I really do dislike that term), so this feature would be somewhat nice to have.

I'd be happy with either a copy constructor (e.g. "new Obj(obj)") or some function that returns a copy (e.g. "obj.clone()").

Implementing this in an ASH function wouldn't at all be feasible since we'd need an overloaded form of the function for each distinct record.

Actually, implementation in Java isn't exactly trivial, either. Ideally there'd be a way to create a shallow copy as well as a deep copy (in the case that you have records with other records/arrays/maps as fields), but that's a substantially larger undertaking.

Either way, at the very least, in the underlying architecture, there's a way to iterate over the field names, so it'd probably be more straightforward and elegant than attempting this in ASH.
 

heeheehee

Developer
Staff member
ASH doesn't support creating circular structures just yet (and it wouldn't handle them very well -- there's no 'null' in this language), so that should make the given implementation somewhat simpler (for deep copies, that is). (ASH records didn't provide themselves in their own scopes when I last checked a few months back -- so linked list implementations aren't possible as it stands. But once again, we'd need null in order to not freak out the interpreter.)

An alternative to this (or possibly an extension), would be creating some way to iterate over the fields of a record (but this might be a separate feature request).

Also, one thing I discovered when playing around with records and attempting to create custom constructors is that once a record is declared, its constructor takes priority over all functions with the same name -- if you attempt to call any of those functions, it'll run into a script parsing error since "new" doesn't come right before the call to the record's constructor.
 

Winterbay

Active member
I agree. Not having to write a a special function for every record in a script to be able to copy it would be awesome and make my life a lot easier.
 

jasonharper

Developer
Proposal: add x from y; as a new assignment operator, syntactically equivalent to x = y; except that it recursively copies elements from the RHS into the existing LHS, rather than entirely replacing the LHS. "from" is already a keyword, so this can't break existing scripts. As an operator rather than a function, it would be possible to control the implementation type of the result - in particular, you could copy a plural constant into a modifiable map. Also, you could use it to merge maps with disjoint keys. The downside is that it would have no way of specifying the depth of the copy, it would always be a deepcopy.
 
ASH doesn't support creating circular structures just yet (and it wouldn't handle them very well -- there's no 'null' in this language)
This gets me every time. I see that there is a "new" so I assume that I can use null as well. Recently in fact. EDIT: In fact my latest report (regarding group() resulting in "0") stemmed from a script where I'm trying to find a way around the lack of a linked list. Working out quite nicely.
An alternative to this (or possibly an extension), would be creating some way to iterate over the fields of a record (but this might be a separate feature request).
How delicious this sounds: foreach type, identifier, value in record{}?

Also, one thing I discovered when playing around with records and attempting to create custom constructors is that once a record is declared, its constructor takes priority over all functions with the same name -- if you attempt to call any of those functions, it'll run into a script parsing error since "new" doesn't come right before the call to the record's constructor.
I did not even know ASH had constructors. So... functions with the same name as a record type become the constructor by default? And are they allowed to take in parameters? something like "var1 = new recordName(blah, blah)"?
 
Last edited:

zarqon

Well-known member
Yes. This is not documented in an easy-to-find place, but ASH does have constructors for records, just as you described. You can't use them if your records have maps in them though, since there is no constructor for maps. That would be another delicious feature request.
 
Yes. This is not documented in an easy-to-find place, but ASH does have constructors for records, just as you described. You can't use them if your records have maps in them though, since there is no constructor for maps. That would be another delicious feature request.

Can't use as in they generate an error? Couldn't you just initialize the map to nothing?

Am I correct to assume that "this" is the identifier for the record being constructed?
 

Winterbay

Active member
Well, you can use them if you predefine the map, but I guess that defeates the purpose slightly:
Code:
record bonk
{
	int one;
	int two;
	int[int] three;
};

bonk bonk(int one, int two, int[int] three)
{
	bonk test;
	test.one = one;
	test.two = two;
	for i from 0 to count(three)
		test.three[i] = three[i];
	return test;
}

void main()
{
	int[int] line;
	for i from 0 to 3
		line[i] = i;
	bonk mine = new bonk(1,2,line);
	print("The record contains the following values: " + mine.one + ", " + mine.two + ", " + mine.three[0] + ", " + mine.three[1] + ", " + mine.three[2] + ", " + mine.three[3]);
}

The above code generates
Code:
The record contains the following values: 1, 2, 0, 1, 2, 3
 
Ah, I see what you mean.
I suppose my question then is could you create the constructor such that it doesn't take in all fields?

for instance
Code:
bonk bonk(int one, int two){
 bonk test;
 test.one=one;
 test.two=two;
 clear test.three;
 return test;
}
 

Winterbay

Active member
Well... Clear did not work (it complained about unkown variable clear) but just comenting out that part (and changing the function call to take 2 parametares) leads to:
Code:
The record contains the following values: 1, 2, 0, 0, 0, 0

So, test.three got set to 0s since no other value was given (changing the definition of three to a sting[int] gives empty strings instead and so on).
 

slyz

Developer
Does
PHP:
bonk mine = new bonk(1,2,line);
still work if you don't define bonk(int one, int two, int[int] three)?
 

Winterbay

Active member
Hmm... I may have misunderstood something...
Code:
record bonk
{
	int one;
	int two;
	int[int] three;
};

bonk bonk(int one, int two, int[int] three)
{
	bonk test;
	test.one = one * 2;
	test.two = two * 2;
	for i from 0 to count(three)
		test.three[i] = three[i] * 2;
	return test;
}

void main()
{
	int[int] line;
	for i from 0 to 3
		line[i] = i;
	bonk mine = new bonk(1,2,line);
	bonk miner = bonk(1,2,line);
	print("The record contains the following values: " + mine.one + ", " + mine.two + ", " + mine.three[0] + ", " + mine.three[1] + ", " + mine.three[2] + ", " + mine.three[3]);
	print("The record contains the following values: " + miner.one + ", " + miner.two + ", " + miner.three[0] + ", " + miner.three[1] + ", " + miner.three[2] + ", " + miner.three[3]);
}

Gives the following:
Code:
> call test.ash

The record contains the following values: 1, 2, 0, 1, 2, 3
The record contains the following values: 2, 4, 0, 2, 4, 6

Edit: Running that code without the function will lead nothing since it won't verify.
 
Last edited:

slyz

Developer
What I meant is that doing this works:
PHP:
record bonk
{
	int one;
	int two;
	int[int] three;
};

int[int] line;
for i from 0 to 3 line[i] = i;

bonk mine = new bonk(1,2,line);

print("The record contains the following values: " + mine.one + ", " + mine.two + ", " + mine.three[0] + ", " + mine.three[1] + ", " + mine.three[2] + ", " + mine.three[3]);
Code:
> call test.ash

The record contains the following values: 1, 2, 0, 1, 2, 3
 

heeheehee

Developer
Staff member
Proposal: add x from y; as a new assignment operator, syntactically equivalent to x = y; except that it recursively copies elements from the RHS into the existing LHS, rather than entirely replacing the LHS. "from" is already a keyword, so this can't break existing scripts. As an operator rather than a function, it would be possible to control the implementation type of the result - in particular, you could copy a plural constant into a modifiable map. Also, you could use it to merge maps with disjoint keys. The downside is that it would have no way of specifying the depth of the copy, it would always be a deepcopy.
I don't know, I feel like I'd rather have a way to iterate over keys of a record as well as a "typeof" (although right now, we have a crude version involving coercion to strings. :D)

Also, one thing I discovered when playing around with records and attempting to create custom constructors is that once a record is declared, its constructor takes priority over all functions with the same name -- if you attempt to call any of those functions, it'll run into a script parsing error since "new" doesn't come right before the call to the record's constructor.
This is evidently a lie, as evidenced by Winterbay's and bordemstir's experimenting. Either it was fixed at some point (which I do not recall seeing in the commit logs, so it might've been a side effect), or I was just doing something wrong.
 

jasonharper

Developer
I don't know, I feel like I'd rather have a way to iterate over keys of a record

Utterly impossible, the way ASH currently works: inside the iterator, you'd have a variable that potentially changes type on each iteration. The compiler wouldn't know what to do with such a thing, and couldn't determine which version of any overloaded functions to apply to it. The closest that we could come would be a read-only iteration that gives you stringified versions of all values; that might actually be useful (you'd be able to dump arbitrary objects for debugging purposes), but wouldn't help as far as copying objects goes.
 

heeheehee

Developer
Staff member
Utterly impossible, the way ASH currently works: inside the iterator, you'd have a variable that potentially changes type on each iteration. The compiler wouldn't know what to do with such a thing, and couldn't determine which version of any overloaded functions to apply to it. The closest that we could come would be a read-only iteration that gives you stringified versions of all values; that might actually be useful (you'd be able to dump arbitrary objects for debugging purposes), but wouldn't help as far as copying objects goes.

Right, I keep forgetting that these things are just entirely different with strongly typed languages. I would imagine that it'd be more convenient (or at least more maintainable) to just add some function rather than reuse a keyword, but I'm not the developer here, so I'll defer to your expertise.
 

heeheehee

Developer
Staff member
Bump with a proposed patch (see attached).

Proposed new ASH functions:
Code:
aggregate copy(aggregate src, aggregate dest, boolean deep);
record copy(record src, record dest, boolean deep);

Currently no safety checks, so I have no idea what happens if, for instance, src and dest are different records and/or aggregate-types. This probably needs to be fixed. Also, return value is useless, but I kept it just because it made the code shorter. edit: actually, they could both return "void", I suppose (I don't know if there are any checks).

Only minimally tested, but it does work with nested arrays/records :)
 

Attachments

  • copy.patch
    2.4 KB · Views: 28
Top