Array literals and script only stack initialization (was Re: Developing first on android)

Mark Waddingham mark at livecode.com
Mon Aug 21 05:58:27 EDT 2017


On 2017-08-21 01:31, Monte Goulding via use-livecode wrote:
> Right now you need to handle preOpenStack in stack A and setup the
> hierarchy because stack B does not retain the behavior property when
> saved and does not get any kind of message when used as a behavior. It
> may actually be a good idea to add a loadBehavior and unloadBehavior
> message sent to the object using the behavior so it can initialise its
> own heirarchy. Still I think saving the behavior property with the
> script only stack is a simpler solution.

I think its been suggested before that a 'loadStack' message might be 
quite useful (perhaps it should be 'createStack' - we have 'deleteStack' 
which is probably sufficient for the antonym) - allowing a stack to 'do 
something' when it is loaded into memory, and then unloaded (equivalent 
to OnCreate / OnDestroy in LCB) as loading/unloading is a slightly 
different part of the life-cycle compared to opening and closing.

I can certainly see that it would be a 'simpler' solution in some ways 
to save the behavior property - however, then it isn't 'just' a script 
anymore - it is actually just a cut down object, so that leads us into 
having general properties / structures encoded in a 'script only stack'.

I was playing about with prototyping something yesterday - array 
literals - it has been suggested before (indeed I thought there was a 
partial attempt by someone before - but I couldn't find it) but I 
thought it would be interesting to try it: 
https://github.com/livecode/livecode/pull/5824

Basically this patch adds JSON-like (which is LCB-like too!) syntax for 
sequence (numerically keyed array) and array (associative array) 
literals:

   put [ 1, 2, [ 3, 4, { "size" : the length of tOtherSeq } ] ] into 
tSeqVar
   put { "foo" : "bar", "baz" : tMyValue , tMyKey : [ 1, 2, 3 ] } into 
tArrVar

The syntax is the same as in LCB - keys and values can be static or 
constant. The implementation has the nice property (well, the sequence 
version does - the array one isn't quite finished yet) that actual 
constant literals (those which have constant key/values which can be 
evaluated at compile time) will share the value. i.e.

   put [ 1, 2, 3 ] into tSeqVar1
   put [ 1, 2, 3 ] into tSeqVar2 -- this will actually share the 
in-memory value with tSeqVar1

This means that you can explicitly write the same constant anywhere, as 
much as you like in script and still only have one instance of it. 
Indeed, the performance of using array literals compared to separate 
'put' statements to construct them is quite encouraging:

    BenchmarkStartTiming "LegacyCreation"
    repeat kRepetitions times
       get empty
       put true into tLegacyLiteral[1]
       put 1 into tLegacyLiteral[2]
       put pi into tLegacyLiteral[3]
       put "Hello" into tLegacyLiteral[4]
       put false into tLegacyLiteral[5]
       put 2 into tLegacyLiteral[6]
       put pi into tLegacyLiteral[7]
       put "World!" into tLegacyLiteral[8]
       put it into tLegacyLiteral[9]
    end repeat
    BenchmarkStopTiming

    BenchmarkStartTiming "ConstantCreation"
    repeat kRepetitions times
       get [ true, 1, pi, "Hello", false, 2, pi, "World!", [ true, 1, pi, 
"Hello", false, 2, pi, "World!" ] ]
    end repeat
    BenchmarkStopTiming

    BenchmarkStartTiming "LegacyDynamicCreation"
    repeat kRepetitions times
       put empty into tLegacyLiteral
       put _Identity(true) into tLegacyLiteral[tOne]
       put _Identity(1) into tLegacyLiteral[tTwo]
       put _Identity(pi) into tLegacyLiteral[tThree]
       put _Identity("Hello") into tLegacyLiteral[tFour]
    end repeat
    BenchmarkStopTiming

    BenchmarkStartTiming "DynamicCreation"
    repeat kRepetitions times
       get [ _Identity(true), _Identity(1), _Identity(pi), 
_Identity("Hello") ]
    end repeat
    BenchmarkStopTiming

Note: In the 'LegacyDynamicCreation' test variables holding actual 
numbers and not numeric literals are used as that is a fairer test with 
how things work at present (a technical detail - literals such as 1 are 
actually stored internally ready for being used as an array key; whereas 
dynamic creation has to create a key from the actual index 1).

On my machine I get the following results:

	LegacyConstantCreation 3037 ms
	ConstantCreation 53 ms
	LegacyDynamicCreation 10330 ms
	DynamicCreation 6496 ms

So there's certainly a significant performance advantage.

My only concern is introducing more symbols. There is another option 
here using ( ) can also be made unambiguous:

   ( expr ) - grouped expr

   ( expr , ) - single element sequence

   ( expr : expr ) - single key/value

However, I wonder if this is a bit too subtle - it would be all too easy 
to use ( expr ) for when you actually want a single element list - no 
amount of context can really help here.

This also overlaps with recent discussions about strings. The syntax of 
literals above is the same as JSON (and can be made compatible with 
Python, just by allowing a trailing ',' in lists) *except* that LCS 
strings are not escaped so they aren't necessary copy/paste comparison. 
Therefore it does suggest that making "..." escaped strings as 
cross-compatible with other languages as possible is maybe something to 
seriously consider (if we wanted Python cross-compatibility '...' type 
strings would have to be too - although Python has about 6 variants of 
strings, you can use letter prefixes before the quote to change the 
interpretation of the string).

Anyway, getting back to the original point of the original post... The 
reason this might be of interest to the problem of script only stacks is 
that with a suitable 'loadStack' type method, you could do something 
like:

on loadStack
   set the properties of me to { \
     ... key/value pairs of properties ... \
   }
end loadStack

Indeed, if we had a better 'properties' property (i.e. as suggested by 
me in terms of import/export array - something allowing you to get/set 
the core persistent state of an object directly) then you could have 
script-only-stacks which *also* contain their state (and, by collorary, 
state of objects on the stack) - via having code which does it.

Warmest Regards,

Mark.

-- 
Mark Waddingham ~ mark at livecode.com ~ http://www.livecode.com/
LiveCode: Everyone can create apps




More information about the Use-livecode mailing list