Translation Please: telling a reference to an object

At some point in the past, I copied this Search&Replace handler without really studying it, but now don’t understand exactly how the first part works.

on snr(the_string, search_string, replace_string)
	tell (a reference to my text item delimiters)
		set {old_atid, contents} to {contents, search_string}
		set {the_string, contents} to {the_string's text items, replace_string}
		set {the_string, contents} to {"" & the_string, old_atid}
	end tell
	return the_string
end snr

In particular, what is the purpose of this: “tell (a reference to my text item delimiters)”?

I understand that “set myRef to a reference to myLongStringOrList” allows me to use the contents property to refer to the entire object, in this case the calling script’s text item delimiters. What does the “telling” have to do with it?

I understand what the rest does: it is using the search_string as a delimiter and replacing it in the the_string with replace_string where {“” & the_string} just coerces whatever type the string might be to text.

There are lots of other examples in this forum of "telling an object like a window, for example. When, in general does one do that?

AppleScript is the last target, so when you write:

set v to text item delimiters

it’s the same as:

set v to text item delimiters of AppleScript

When you get a reference to the object with something like:

set v to a reference to text item delimiters of AppleScript

It’s a pointer to the object, and you can modify its contents.

gl,

Thanks, Kel;

Got that. By referencing the object you are able to switch it around conveniently.

Is that all the “tell” does? Is the tell group just to bracket all the instructions that refer to contents so they have the same basis (and thereby avoid appending “AppleScript’s text item delimiters” to every instruction that refers to them)?

Yes.

[snip code]
It’s a rather pretentious, gratuitously obfuscated and completely unwarranted way to write:

on find_and_replace(the_string, search_string, replace_string)
	set old_atid to text item delimiters
	set text item delimiters to search_string
	set temp_list to the_string's text items
	set text item delimiters to replace_string
	set the_string to temp_string as Unicode text
	set text item delimiters to old_atid
	return the_string
end find_and_replace

K.I.S.S.

Nope. The ‘a reference to’ operator is used to create a reference object that identifies a property or element of an object. Reference objects can be a bit confusing to wrap your head around at first, and are easiest to understand by example. Let’s take a list containing several elements:

set lst to {1, 2, 3, 4, 5}

Most times when you write a reference identifying an element in the list, you want that reference to be resolved immediately, returning the value stored at that location:

set val to item 3 of lst -- variable 'val' contains the value '3'

Sometimes though you don’t want to get the value stored at a particular location straightaway; instead you just want to keep a note of where that location is so you can fetch a value from it later on. So you create a reference object to that location instead:

set valRef to a reference to item 3 of lst -- variable 'val' contains the reference object 'item 3 of <the list object>'

Later on, to resolve the reference, you ask the reference object for the value of its ‘contents’ property:

set val to valRef's contents --> the value [i]currently stored[/i] at item 3 of the list

What’s the significance of all this? Well, compare the two following examples:

set lst to {1, 2, 3, 4, 5}
set val to item 3 of lst
set item 3 of lst to 0
val --> 3
set lst to {1, 2, 3, 4, 5}
set valRef to a reference to item 3 of lst
set item 3 of lst to 0
set val to valRef's contents
val --> 0

The real advantage is that you can manipulate reference objects just like any other objects: store them in variables, pass them to handlers, and so on. e.g. You’ll often use them in ‘repeat’ blocks to manipulate a list’s content in-place, e.g. to change a list of Finder references into a list of aliases without having to build a second list:

repeat with itemRef in finderFileRefsList
    set itemRef's contents to itemRef's contents as alias
end repeat

For most tasks you’ll never need to create your own reference objects though as this is really only useful in advanced programming tasks (or for showing off one’s l33t h@X0r credentials, I suppose).

Tell blocks are generally used to specify a common target for a whole bunch of commands to be sent to. (Most often that target is an application object, though any object is acceptable.) You can also refer to the target object’s properties by using the ‘its’ keyword:

set rec to {a:1, b:2, c:3}
tell rec
	get its b --> 2
end tell

(Note that the ‘its’ keyword can be omitted in some cases, though is required in others.)

Tell blocks are really just a stylistic device as there’s other ways to achieve exactly the same ends. While tell blocks can be good for reducing the size and complexity of code in some cases, gratuitous usage tends to increase code complexity instead, making code harder to read and understand, increasing the chances of it containing bugs and making it harder to modify in future.

When it makes code easier to read than writing it some other way. As a rough rule of thumb, if you’re sending several commands to the same object(s) at the same time then your code will be cleaner and more compact if you use a tell block; otherwise don’t bother. Developing good coding style is mostly just a matter of practice; for example, the following examples all do the same thing, so deciding which form to use is really just a matter of picking the one that’s the most readable and least likely to make you tie yourself in knots:

tell application "Finder"
	set fileNames to name of every file of target of front Finder window
	set fileSizes to size of every file of target of front Finder window
	set fileLabels to label of every file of target of front Finder window
end tell

tell application "Finder"
	set filesRef to a reference to every file of target of front Finder window
	set fileNames to name of filesRef
	set fileSizes to size of filesRef
	set fileLabels to label of filesRef
end tell

tell application "Finder"
	tell every file of target of front Finder window
		set fileNames to its name
		set fileSizes to its size
		set fileLabels to its label
	end tell
end tell

HTH

has

Thank you all, and particularly HHas for his very cogent explanation. Several of the concepts explained in his post had not become clear to me in the 3 months since I set out to learn AS.

Ha.

(Disclosure: I may very well be the author of the original code.)

While I agree that HAS’s explanations of references and tell blocks are helpful, his initial statement is bollocks. What is pretentious, gratuitously obfuscated and completely unwarranted about it? Gratuitously obfuscated code would use variable names that have no relation to their contents or handler names that belie their function, this is not the case with the original snippet. In fact, since the code is compact, it is less likely to introduce errors than HAS’s example code which, in fact, is flawed. Try using HAS’s code and you’ll find that you’ve got an error because it’s used a new variable that hasn’t been defined (“temp_string” instead of “temp_list”).

One of the benefits of AppleScript (though some would argue it’s one of the deficits of the language), is that variables are not static by class type meaning that you can assign values of varying types to the same variable. Since all of the work in the original snippet is acting on the same text, even though in the middle it gets burst apart into a list, keeping it assigned to the same variable helps, not hinders, the legibility of the code (at least in my opinion). This is born true by the fact of the error I mentioned above introduced by HAS. I’m not entirely sure how memory is allocated and released in AppleScript but I suspect that keeping the same variable also helps with memory management.

Further, while the original snippet used a handler name of “snr” (a common short form acronym of “search and replace”, more common and understandable than “old_atids” for “old AppleScript’s text item delimiters” which HAS had no problem using in his code), HAS changed the name to “find_and_replace”. By changing the name to no longer match the parameters (he essentially changed “search” to “find” yet kept “search_string” in the parameters instead of “find_string”), he actually added confusion to the code!

Again, I appreciate HAS’s explanations and the fact that he has posted them here for the benefit of others; I hope that he will continue to do so. However, his denigration of the original code (and, really the original code’s author, perhaps me, – he effectively called the person “stupid” by using the “K.I.S.S.” acronym), is what I find to be rather pretentious and unwarranted.

Jon

Well, I will say, after 90 days of AppleScripting, I’m beginning to appreciate the nuances embedded in some of the script examples from Jonn8 and JJ. A little search of my own files reveals that the script handler snr I started this thread with is indeed Jonn8’s. Didn’t mean to start a fight, though.

Wouldn’t know about that; I’ve seen it being posted by various folks in cut-n-paste form for quite a few years now. Don’t know who wrote it and frankly don’t care.

Use of ‘a reference to’ operator. Use of ‘tell’ block. Multiple variable assignments in a single set statement. All of which have their place, but none in something so inherently simple and straightforward as a tid-based find and replace routine. All completely unnecessary here, so the only reason to use them is to demonstrate one’s l33t mastery of such intricate and involved AppleScript arcana.

Take it from one who’s produced plenty of self-indulgently smartarsed and ultimately pointless code in their time, so knows such crud when they see it.

LOC is a spectactularly lousy measurement of, well, anything really. Two or three simple lines of code are usually simpler and easier to follow than a single complex one. e.g. There’s plenty of Perl code that’s both incredibly compact and almost impossible to make sense of simply from looking at it. Often the easiest way to make sense of complex, compacted code is by rewriting it in longer, simpler form!

A careless typo on my part. That particular example was knocked together for illustrative purposes only; the findAndReplace handler I’d actually use is about twice the length as it’s got all sorts of additional error handling and bug workaround code (unfortunately often necessary in AS for vaguely tolerable robustness, but not helpful for pedagogical purposes). If you like, consider that typo a good demonstration of why ad-hoc cut-n-paste/off-the-cuff code is a fundamentally bad thing in itself; I shoved all this low grade nastiness into robust, thoroughly tested and fully reusable libraries years ago, which solves the problem better than anything. (Nothing like eliminating dozens or hundreds of lines of menial repetitive bloat completely for really simplifying a program.) But anyway…

Variable naming is one of those subjects particularly prone to endless dull arguments on good taste and design, naming conventions, religious dogma, and so on. Anyone particularly interested in variable naming issues should go read a copy of Code Complete or something; it’s a trivial issue here.

You should be much more concerned about how readers are meant to follow their way through the convoluted references and assignments going on in the original code - that’s the ‘pretentious/gratuitous/unnecessary’ part. As a simple exercise, try reading the two versions out loud. AppleScript is especially well regarded for its English-like readability, after all, so let’s see which one reads more naturally and takes less brainpower to follow what it does.

Completely irrelevant. Whole point of using a memory managed language is you do not need to think about such things.

(Tip: Anyone who wants to interpret criticism of their work as a personal attack seriously needs to grow up. So I really don’t recommend going there, or there’ll be a real spanking involved.)

Bad code is bad code. Everybody writes bad code at times; the point is learning to tell good code from bad code and avoiding the latter whenever you can. Beating on bad code - especially the persistently bad - is a public service; it deserves no less and certainly no more.

has

“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.” – Brian W. Kernighan

Hi,

As Guardian said, yes you don’t need to keep writing:

AppleScript’s text item delimiters over and over again if you make it the target through the use of the tell block. About the contents, here’s two scripts. The one that uses the ‘a reference to’ operator allows you to change the contents of the original list:

set v to {1, 2, 3}
set vref to a reference to v
copy vref to w
set end of w to 4
v

Here’s the one that doesn’t change the value:

set v to {1, 2, 3}
copy v to w
set end of w to 4
v

gl,

Got it. Interesting that if you look at the results you get this:

set v to {1, 2, 3}
set vref to a reference to v
copy vref to w
set end of w to 4

-- {v, w} -> {{1, 2, 3, 4}, v of «script»}

set x to {1, 2, 3}
copy x to y
set end of y to 4

-- {x, y} -> {{1, 2, 3}, {1, 2, 3, 4}}

That is, the w result is v of «script», not the value.
That’s all nicely explained in Goodman’s Handbook too, I now realize, but I didn’t get its implications.

Thanks

In your example above, variable v is declared at the top-level of your script so is a global variable [1], and you can create references to a script’s global variables just as you can to its properties. It’s simpler to see from this:

set x to 1
set xRef to a reference to x
xRef --> x of «script»

This doesn’t work for local variables, however.

HTH

[1] Note that due to glitches in the AppleScript implementation, global variables declared this way don’t work quite like global variables declared using the conventional ‘global ’ statement, in that you can’t refer to them from within handlers (you [i]must[/] provide explicit ‘global’ declarations for this to work). This often causes additional confusion and is not one of AppleScript’s finer moments.

Tautological. AppleScript may be noted for the English-likeness of its syntax, but “readability” is a matter of opinion. Evaluating code on the basis of its compliance with a dubious virtue attributed to the language it’s written in (often by those who don’t code in it themselves) is a little disingenuous, don’t you think?

There may have been a tad sarcasm to that line (my opinions on the pros and cons of AS syntax are well recorded). Still, AppleScript’s keyword-oriented nature does make it particularly easy to read (an important attribute for its target audience). Point is, there’s code whose structure follows the natural flow of thought, and there’s code that doesn’t, and identifying which is which should be particularly easy here given how easy AS syntax is to read.

It’s subjective to a degree (e.g. what one is already familiar with), but it’s also measurable. A single statement that tangles two unrelated tasks together is likely going to be harder to follow than two separate statements that perform each task separately. e.g. Compare:

set {the_string, contents} to {the_string's text items, replace_string}

to:

set the_string to the_string's text items set contents to replace_string
There are two identifier-expression associations that the reader needs to identify here, one between “the_string” and “the_string’s text items”, the other between “contents” and “replace_string”. To understand the first form, the reader basically needs to mentally (if not physically) reparse it into the second form. So why not save the reader the unnecessary hassle by writing it the second way to begin with? There is no adequate justification for the knottiness of the first example. It’s bad code. Just write what you mean, and don’t try to be clever about it.

There are situations where multiple assignment is appropriate, e.g. short statements where the values being assigned are closely related (e.g. x-y coordinates):

set {x, y} to {400, 800}

or in assigning multiple return values from a handler call to variables:

set {didCancel, theName} to getUserInput("Please enter your name:")

but that top example is not amongst them.

I could provide similar arguments against overuse of tell blocks and reference operators (hint: indirect logic is more work to follow than direct logic; just ask a C programmer what working with lots of pointers can be like) but hopefully the above illustrates the general point.

has

If a relative newcomer to serious use of AppleScript is permitted to leap into what is, I’m sure, a very old fray, then I will say that enforced “readability” can actually be a problem. As I undertook to learn AppleScript a few months ago, I was repeatedly struck by how ill-formed the AppleScript “language” is sometimes - it can be a hodge-podge that doesn’t seem to be founded on a “universal grammar”, and without that grammatical basis, has a large number of “dialects”. After all, Version 1.1 was essentially unchanged after 1993 and was designed to fit in a tiny space, so the dictionaries of applications had to be creative to get things done. It didn’t really evolve after 1993; there were just minor bug fixes and a few new features added in the decade before OS X came along, and I stuck with SuperScript for a lot of what I did.

As I said above, AppleScript’s “dialects” result from its reliance (as it obviously must) on application dictionaries (and their embedded grammers) in scriptable applications. The syntaxes of interactions are not obvious, depending in large part on how “slavishly” the application follows the OOP paradigm (iCal comes to mind as very difficult for me to script with its chain of attributions*, while BBEdit is very straight-forward). Often, in its attempt to be “natural” AppleScript departs from normal expectations for grammar. Having said that, however, I have thoroughly enjoyed learning AppleScript, thorougly enjoyed following this forum, and look forward to the day when intelligent use of XCode is a realistic possibility for me.