Welcome, Guest. Please Login
Tinderbox
  News:
IMPORTANT MESSAGE! This forum has now been replaced by a new forum at http://forum.eastgate.com and no further posting or member registration is allowed. The forum is still accessible via read-only access for reference purposes. If you wish to discuss content here, please use the new forum. N.B. - posting in the new forum requires a fresh registration in the new forum (sorry - member data can't be ported).
  HomeHelpSearchLogin  
 
Pages: 1
Send Topic Print
Scope of local variables wrt each() (Read 2073 times)
BPM
Full Member
*
Offline



Posts: 3
San Jose, CA
Scope of local variables wrt each()
Nov 12th, 2015, 10:54am
 
I think I've uncovered a bug with respect to the scope of local variables in the context of an each() body.  If I declare a local variable and then use that variable within an each() body, the local variable is accessible, but following execution of the each() function, the local variable reverts back to the value it had beforehand.  It seems like local variables are being cloned by each() and then discarded when it's done running.

Here's an example:

Code:
var x(3);
$Text = $Text + "\nInitial x = " + x;
$MyList = "1;2;3;4;5;6;7;8;9;10";
$MyList.each(v) {
	$Text = $Text + "\nLoop Iteration: " + v;
	x = x + 1;
	$Text = $Text + "\n\tx = " + x;
};
$Text = $Text + "\nFinal x = " + x;
 



Using a stamp that simply executes the $Text in the current note, the output is:

Code:
Initial x = 3
Loop Iteration: 1
	x = 4
Loop Iteration: 2
	x = 5
Loop Iteration: 3
	x = 6
Loop Iteration: 4
	x = 7
Loop Iteration: 5
	x = 8
Loop Iteration: 6
	x = 9
Loop Iteration: 7
	x = 10
Loop Iteration: 8
	x = 11
Loop Iteration: 9
	x = 12
Loop Iteration: 10
	x = 13
Final x = 3
 



As you can see, x reverts back to its original value, all changes to it within the each() body being discarded.  I could understand this behavior if another local x had been declared in the each() body, but otherwise it seems like a bug.  I do understand that creating and using an attribute avoids the problem, but since attributes cannot be created programmatically that solution won't work for what I'm currently trying to do.

Best,
Brian
Back to top
 
 
  IP Logged
Mark Anderson
YaBB Administrator
*
Offline

User - not staff!

Posts: 5689
Southsea, UK
Re: Scope of local variables wrt each()
Reply #1 - Nov 12th, 2015, 11:46am
 
I'm not sure if this is a bug given there's no documented behaviour for the case you describe. the fullest reference is here which I based on testing of more limited descriptions in the Tb release notes.

If you want to affect x inside the loop, try something like this:

Code:
if($MyNumber==0) {
   var x(3);
}else{
   var x($MyNumber);
};
$Text = $Text + "\nInitial x = " + x;
$MyList = "1;2;3;4;5;6;7;8;9;10";
$MyList.each(v) {
	$Text = $Text + "\nLoop Iteration: " + v;
	x = x + 1;
	$MyNumber = x;
	$Text = $Text + "\n\tx = " + x + "\n\t$MyNumber = " + $MyNumber;
};
$Text = $Text + "\n~~~~~~~~\nFinal x = " + x + "\n"+
"Final $MyNumber = " + $MyNumber + "\n~~~~~~~~\n\n"; 

now the end of loop #1 records:

~~~~~~~~
Final x = 3
Final $MyNumber = 13
~~~~~~~~


and the end of loop #2:

~~~~~~~~
Final x = 13
Final $MyNumber = 23
~~~~~~~~


You can see x is now starting the next loop using the final value of the previous loop calculation.

I don't know what the real world problem you're solving is but hopefully the latter above gives you a solution. In the meantime, I'll investigate this further off-forum as the solution above does seem cumbersome (even stripping out the text feedback) as you still need to be careful about the starting value of $MyNumber.
Back to top
 
 

--
Mark Anderson
TB user and Wiki Gardener
aTbRef v6
(TB consulting - email me)
WWW shoantel   IP Logged
Mark Bernstein
YaBB Administrator
*
Offline

designer of
Tinderbox

Posts: 2871
Eastgate Systems, Inc.
Re: Scope of local variables wrt each()
Reply #2 - Nov 12th, 2015, 3:54pm
 
This is the intended behavior, but it may not in fact be the desired behavior.

In technical terms, each() creates a closure which clones the values of local variables and adds a new local variable for the loop iterator.  In other words, the local variable frame is passed by value, while you expected it to be passed by reference.

I'll look into whether it's feasible to use reference semantics here.

Can you tell us a bit about the project you're working on?
Back to top
 
« Last Edit: Nov 12th, 2015, 3:56pm by Mark Bernstein »  
WWW   IP Logged
BPM
Full Member
*
Offline



Posts: 3
San Jose, CA
Re: Scope of local variables wrt each()
Reply #3 - Nov 12th, 2015, 6:01pm
 
Hi, Mark.  I only discovered Tinderbox twelve days ago and immediately purchased it.  It has already revolutionized my existence.  It is truly astonishing.  I'm working on implementing a new kind of programming language--something I've been working on since my doctoral days several decades ago--using TB as the front-end with the end goal of automating a significant percentage of both my software development work and my law practice.

With respect to the issue at hand, I understand your explanation but I can't think of a reason why a closure would be useful, since to my knowledge there isn't any way to reference the closure after the each() block is done executing.  I did figure out a workaround using a set-type variable to store the variable's value inside the block:

Code:
var x(3);
$Text = $Text + "\n__OUTPUT__\nInit:  x = " + x;
$MyList = "1;2;3;4;5;6;7;8;9;10";
$MyList.each(v) {
    x = x + 1;
    $MySet.at("x") = x;
};
x = $MySet.at("x");
$Text = $Text + "\nFinal: x = " + x;

__OUTPUT__
Init:  x = 3
Final: x = 13
 



The invaluable TbRef documentation that Mark II put together states under the entry for var():

Quote:
A local variable acts in most ways like a user attribute. Local variables exist for the duration of the action or, when they are declared inside curly brackets { … } their scope is the rest of the clause - i.e the remaining individual statements within the {}.


It goes on to show an example of how a local variable in a block isn't available outside the block:
Code:
$MyNum = 0; {var x; x = 2; $MyNum = x; x=6;};$MyNum = x;
 



But the reason this example doesn't work is because TB doesn't recognize naked blocks at all.  To wit:
Code:
$Text = $Text + "\nBEFORE THE BLOCK";
{
    $Text = $Text + "\nINSIDE THE BLOCK";
};
$Text = $Text + "\nAFTER THE BLOCK";
 


prints this:
Code:
BEFORE THE BLOCK
AFTER THE BLOCK
 



Adding an if(true) causes the block to be recognized:
Code:
$Text = $Text + "\nBEFORE THE BLOCK";
if (true) {
    $Text = $Text + "\n\tINSIDE THE BLOCK";
};
$Text = $Text + "\nAFTER THE BLOCK";
 


resulting in:
Code:
BEFORE THE BLOCK
    INSIDE THE BLOCK
AFTER THE BLOCK
 



I understand that if blocks weren't implemented using closures, since
Code:
if(true) {
    var x(42);
    x = 1337;
    $Text = $Text + "\n  Inside: x = " + x;
};
$Text = $Text + "\nOutside: x = " + x;
 


yields:
Code:
 Inside: x = 1337
Outside: x = 1337
 



It is surprising that having multiple var() statements declaring the same local variable doesn't yield an error.  What's more surprising is that action() statements don't have access to the note's variable frame at all:
Code:
var x(42);
action('$Text = $Text + "\n Inside: x = " + x;');
$Text = $Text + "\nOutside: x = " + x;
 


Output:
Code:
 Inside: x = x
Outside: x = 42
 



But eval() does:
Code:
var x(42);
x = eval('x+1295');
$Text = $Text + "\nResult: x = " + x;
 


Result:
Code:
Result: x = 1337
 



The fact that action() doesn't carry the current frame with it is unfortunate, since it forestalls the possibility of using actions as parameterized methods with local variables holding the arguments.

I'm not sure whether I should start another thread or not for this, but I have some additional questions:

- Can one programmatically retrieve the type[i] of a variable/attribute (e.g., typeof(x))?
- Can one programmatically retrieve a list of the attributes for a given note?
- Can one programmatically add/remove attributes from a given note?
- Is it possible to programmatically explode a note?
- Is there a way to interact with the window tabs and switch their view modes programmatically?
- Is it possible to change the width of the text window from code?
- Is there a way to retrieve/modify the raw rtfd contents of a note's $Text [i]in situ
, i.e., not by parsing the XML file?
- Can the actual $Text contents (or any other attribute) of a note be retrieved via a tinderbox:// URL?
- What are the available options for the "pane designators" referred to in the TbRef page covering the TB URL schema?
- Is there a way to have multiple windows open of a document and the text panes not by sync'd to each other?
- Is there a way to access information within TB from JavaScript from within the Preview pane?  I know it's potentially feasible since I've built WebView interfaces that provide that capability.
- Is there a way to create a locked note in map view that will act like a button, triggering functionality when it's clicked?
- Is there a way to retrieve/modify the class, URL, target, and title contained in a map link from code?
- Is there a way to right-align columns in outline view?
- Is there a way to enable columns and/or checkboxes only for a particular group?
- Is there an sdef file for TB?
- Is there a way to reorder the list of stamps?

Sorry for the long list, but I'm in waaay deep and didn't want to forget anything.  I don't mean to distract you from working on this incredible app, so please respond, if at all, only at your leisure.

Brian
Back to top
 
 
  IP Logged
Mark Anderson
YaBB Administrator
*
Offline

User - not staff!

Posts: 5689
Southsea, UK
Re: Scope of local variables wrt each()
Reply #4 - Nov 13th, 2015, 4:38am
 
As you clearly have programming experience, it is worth noting that Tinderbox action code is not - and was never designed as - a fully formed programming environment. What you see is iterative development (over 14 years) of what was a very simply macro mark-up system to aid exporting note to HTML; one of Tinderbox's early purposes was as a weblog editor (pre database-driven blogs, or blog as a common term!).

Your observation about closures reminds me that {} brackets on their own have no defined meaning - certainly not the block concept as you describe. I suspect my aTbRef comment is poorly worded in that using {} to define var scope is probably the only such use of {}. Otherwise they are only used as part of the syntax for marking if/else branch code and each() loop code.

It's passed from mind what user's problem led to var() being added but likely it is used by few and unsurprisingly lightly tested in edge case terms.

Given the way action code evolved, these issues are less bugs than newly-emergent desired functionality. If you think in those terms you may be less frustrated. Rather than beating the bounds of a tightly defined feature area, we're actually just exploring an ever-expanding uncharted outer perimeter. It took me a long time to see that, so don't take my comment as a criticism.

I'll take your idea of a new thread to answer the list of questions, if only because all the above would be a confusing preamble to the list for later readers.

The thread answering the question list is here.
Back to top
 
« Last Edit: Nov 13th, 2015, 5:52am by Mark Anderson »  

--
Mark Anderson
TB user and Wiki Gardener
aTbRef v6
(TB consulting - email me)
WWW shoantel   IP Logged
BPM
Full Member
*
Offline



Posts: 3
San Jose, CA
Re: Scope of local variables wrt each()
Reply #5 - Nov 13th, 2015, 6:04am
 
Hi, Mark.  Please don't interpret my comments as disparaging the system.  I'm a language junkie and am not frustrated at all, rather I'm brimming with enthusiasm.  I appreciate the fact that the language evolved as a secondary aspect of the system.  As it currently stands, I think it's fantastic; the concise way of specifying designators is particularly brilliant, as is the absence of control flow constructs that could lead to infinite loops, as is the template mechanism.  It would be nice to have the ability to define parameterized methods (I have yet to fully grok macros, so maybe they'll suffice), Perl-like hashes, JSON and/or XML decoding, variable interpolation, quote escaping, exception handling, and a += operator, but none of those things are essential; I've implemented several enhancements already as source code filters, which the extraordinarily flexibility of the system makes it remarkably easy to do.

There are aspects of the system that I would like to understand better, and that's the reason I joined the forum and the reason for my posts.  For example, to replace smart quotes, em-dashes, and ellipsis characters with regular ASCII, this works:

Code:
var input("He said, “Fnord…” — but who was ‘he’?");

input = input.replace("(“|”)",'\"').replace("(‘|’)","'").replace("…","...").replace("(—)", "--");

$Text = $Text + "\n\n" + input;
 



but using a regex set operator immediately crashes the app:

Code:
var input("He said, “Fnord…” — but who was ‘he’?");

input = input.replace("[“”]",'\"').replace("[‘’]","'").replace("…","...").replace("—", "--");

$Text = $Text + "\n\n" + input;
 



and changing the replacement value for smart double quotes from \" to just a " causes the code to stop running (I suspect because the parser somehow thinks a double quote is missing):

Code:
var input("He said, “Fnord…” — but who was ‘he’?");

input = input.replace("(“|”)",'"').replace("(‘|’)","'").replace("…","...").replace("—", "--");

$Text = $Text + "\n\n" + input;
 



Best,
Brian
Back to top
 
 
  IP Logged
Mark Anderson
YaBB Administrator
*
Offline

User - not staff!

Posts: 5689
Southsea, UK
Re: Scope of local variables wrt each()
Reply #6 - Nov 13th, 2015, 6:43am
 
Lovely to see someone having such fun. My perhaps over-defensive were more by way of managing expectations/assumptions. Depending on your start perspective, some things are less confusing - or frustrating then might otherwise be the case.  Smiley

Ha - your latest examples hit on the thorny area of escaping quotes. about the hardest thing to do. As all TB data (or pre RTF text) including attribute values, action code is stored as valid XML and then parsed in an out of that, quotes can be an issue. Also, in case not mentioned, action code only accepts straight single/double quotes as string delimiters; typographic quotes are simply some unicode character with no code relevance. This will bite as note $text is optimised for the writers who generally never want to see straight quotes. Tip, if plying with TB code in $Text use the built-in 'Code' prototype for such note as it optimises away writing-centric annoyances like changing quote styles unasked.

Anyway, escaping quotes is not possible and replacing them is something you'll normally need to do with care and probably in 'long' form as per your first example. Just file it as 'one of those things' to accept! FWIW, TB exports UTF-8 in most cases so you only ned to change quotes, dashes, ellipses, etc., in text if the downstream process can't handle unicode. Some old examples online might give the wrong impression. Since mid v5 and certainly in v6 there should be no non-Unicode capable processes. v6 moving off old pre-Cocoa frameworks seems to have stomped some old Unicode related issues.

BTW, your examples don't need a 'var'. You can chain dot-operators to strings.

"He said, “Fnord…” — but who was ‘he’?".replace("(“|”)",'"').replace("(‘|’)","'")

If you want to ensure the string is properly detected or to first evaluate fully the chained-to item, use parentheses:

("He said, “Fnord…” — but who was ‘he’?").replace("(“|”)",'"').replace("(‘|’)","'")
("He said, “Fnord…” — "+$MyString).replace("(“|”)",'"').replace("(‘|’)","'")

To hold interim values, a general TB style is to use attributes:

$MyString = ("He said, “Fnord…” — but who was ‘he’?").replace("(“|”)",'"');
$Text = $Text + "\n\n" + $MyString;


This sort of chunking is good for learning/debugging but you can go all-in-one:

$Text = $Text + "\n\n" + ("He said, “Fnord…” — but who was ‘he’?").replace("(“|”)",'"');

Indeed in the latter case (not tested!) because of all the quotes and the preceding concatenation as TB works left to right you might want some extra parentheses on the last section to control evaluation order.

$Text = $Text + "\n\n" + (("He said, “Fnord…” — but who was ‘he’?").replace("(“|”)",'"'));

The latter's makes it plain to TB you want the evaluate result of the replace concatenated to preceding strings.

If you read into aTbRef, you'll see there's a fair degree of type coercion. If you give TB something unexpected, TB will attempt to coerce a pertinent usable value from what's passed.

TB is 'quiet' in terms of error. A new v6 feature will warn if some particular context for action code (queries, rules, etc.) can't be parsed but generally you get a silent fail. If you get an egregious result or (rarely!) a crash do let support know (with any log and originating context).

Going back to variables, in TB if not starting with a literal string/number/etc, you start with an attribute value. that value can be that of the current note or that in another note: $MyString vs. $MyString("Other note"). In the latter case the value is fetched from a note called other note. The offset references is actually the $Path to that note but as long as the offset references $Name is unique using $Name as a (shorter!) path is almost invariably a correct and more manageable option.

Wishing you lots of fun exploring Tinderbox!
Back to top
 
 

--
Mark Anderson
TB user and Wiki Gardener
aTbRef v6
(TB consulting - email me)
WWW shoantel   IP Logged
Mark Bernstein
YaBB Administrator
*
Offline

designer of
Tinderbox

Posts: 2871
Eastgate Systems, Inc.
Re: Scope of local variables wrt each()
Reply #7 - Nov 13th, 2015, 10:04am
 
Quote:
- Can one programmatically retrieve the type[i] of a variable/attribute (e.g., typeof(x))?

Nope. This wouldn't be difficult, if there were a use case.

Quote:
- Can one programmatically retrieve a list of the attributes for a given note? - Can one programmatically add/remove attributes from a given note?

No, because all attributes are defined for every note.


Quote:
- Is it possible to programmatically explode a note?


No. The hazard of iterative or recursive explosions is frightening. Again, could be done, I suppose, but I'm not sure this would be widely used.

Quote:
- Is there a way to interact with the window tabs and switch their view modes programmatically? Is it possible to change the width of the text window from code?


Nope. This sort of user-interface manipulation is probably best accomplished with scriptability. Our experience of scripting support has not really been favorable.


Quote:
- Is there a way to retrieve/modify the raw rtfd contents of a note's $Text [i]in situ, i.e., not by parsing the XML file?

Nope. Offhand, I'm not entirely sure why you'd want to.

Quote:
- Can the actual $Text contents (or any other attribute) of a note be retrieved via a tinderbox:// URL?


Not yet. This ''is'' in the cards and would clearly be useful.


Quote:
- What are the available options for the "pane designators" referred to in the TbRef page covering the TB URL schema?


The handful currently supported are in the release notes somewhere, or perhaps the backstage Notes.tbx.  (You'll want to take a look at the backstage program: http://www.eastgate.com/Tinderbox/TinderboxSix.html)

Quote:
- Is there a way to have multiple windows open of a document and the text panes not by sync'd to each other?


Lots of moving parts on this one. Right now, the ''selection'' is shared among all open windows; it's possible that the selection should actually be a window property, not a document property.


Quote:
- Is there a way to access information within TB from JavaScript from within the Preview pane?  I know it's potentially feasible since I've built WebView interfaces that provide that capability.


You're thinking of a Tinderbox object in Javascript, so you could write something like Tinderbox.document["Research.tbx"].note["/archives/May/15"].Text ?  This would also be nifty to have. You tell me: how much work would be involved?

Quote:
- Is there a way to create a locked note in map view that will act like a button, triggering functionality when it's clicked?


No. It would not be difficult to add an OnSelect action, which would give you a (rather poor) button like functionality.

Quote:
- Is there a way to retrieve/modify the class, URL, target, and title contained in a map link from code?


Nope. Again, I worry about recursive runaway linking.

Quote:
- Is there a way to right-align columns in outline view?
- Is there a way to enable columns and/or checkboxes only for a particular group?


Nope. Though I'd not be averse.


Quote:
- Is there an sdef file for TB?


Nope. There's not much AppleScript support at all, in part because early experiences left us in some doubt that it's cost-effective, in part because there are more pressing needs.  Again, this is subject to change.

Quote:
- Is there a way to reorder the list of stamps?

Nope. That's just a blunder. Most people don't need very many stamps anyway, and the current interface is obviously best suited to 7±2 stamps.  This will be addressed shortly; you can edit the XML (keep backups!) in the meantime.
Back to top
 
 
WWW   IP Logged
Pages: 1
Send Topic Print