• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: AS Library Question
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: AS Library Question


  • Subject: Re: AS Library Question
  • From: Chris Page <email@hidden>
  • Date: Fri, 18 Dec 2015 03:16:17 -0800

TL;DR: Handlers are objects, just like any other AppleScript value—also, file radars.

On Dec 17, 2015, at 10:52 AM, has <email@hidden> wrote:

On 17/12/2015 12:54, Chris Page wrote:

On Dec 6, 2015, at 9:12 AM, has <email@hidden> wrote:

While handlers may be implemented as objects of type `handler`, that's purely an internal implementation detail.

That is incorrect (or, at least, overstating the case): while it may not be well-documented, the existence of handlers as objects is part of the public interface of AppleScript, and of the OSA API.
…

Yeah, I'm aware of the OSA API's capabilities. However, I'm trying to keep it sufficiently simple for everyone else to understand.

You brought these details up, and your comments deserved a response.

I think we should leave it to each reader to decide whether or not it's interesting to them. We're not standing in a crowded room; it's reasonable to post something to a list that may have a narrower audience.

Others sometimes start off-list discussions with me because they think it may be “too technical” for this list, but that just deprives subscribers of the opportunity to learn and participate.

Specifically, I think that knowing that all AppleScript values are objects, including handlers (and scripts!), is important for anyone interested in AppleScript to know. This is conceptually simpler than leaving someone to think that handlers are somehow different from every other type of value.

e.g., in AppleScript we can send a message to a handler like so:

on foo(x)
x
end foo

set bar to foo
tell bar to foo(42) --> 42
From a message-passing POV, AS doesn't 'send a message to a handler'…

The above code does exactly that—that's why I posted it. It sends the “foo(42)” message to a handler.

Incidentally, if you actually think about the above example, it really makes no logical sense: if «handler foo» were actually being bound to variable `bar`, which is what the code claims to be doing…

That is exactly what the code is doing. As it happens, I was looking at the message-passing implementation code just hours before I wrote that. I wrote it to illustrate the behavior, not to discover it.

… then you'd actually invoke it using `bar(42)`, not `foo(42) of bar` - which is akin to writing `foo(42) of «handler foo»` and thus makes no sense at all.

That is incorrect.

You are thinking in terms of “calling a function”, but that is not what that code does. That line of code sends an “invoke function foo with argument 42” message to the handler, just as writing “make new document” sends a “make with the ‘new’ argument set to ‘document’” message.

When “foo” is either a user identifier or a property identifier, the syntax “foo of bar” asks for the value of that property of bar, whereas a function-call _expression_ like “foo(42) of bar” sends an “invoke this function” message to bar, just as “count of bar” (“count” is an event identifier) sends a “count” message to bar. If you send that message to a script object it looks up the handler, but if you send it to a handler, it executes the handler without any further dispatch (if it matches the handler's name).

That's why the next line in the code—which you omitted from your reply—returns an error: because the “foo” handler only responds to the “foo(x)” message, not the “bar(x)” message:

tell bar to bar(42) --> error "«handler foo» doesn’t understand the “bar” message." number -1708 

As you can see, the error message says that the handler doesn't understand the message. This further illustrates that it sent the message to the handler.

Note that this syntax

bar(42) --> 42

also works in the script I posted. However, it still isn't looking up the value of “bar” as a separate step; rather, it is sending a “bar(42)” message to the current script, and since this code is in the top-level implicit run handler, “bar” is a global, and globals are properties, and handler definitions merely define a property whose value is a handler, so it's just sending the message to the current script and finding the script's handler. If you move that code into some other handler, where “bar” is just a local variable, that line fails:

on foo(x)
x
end foo

on test()
set bar to foo
tell bar to foo(42) --> 42
bar(42) --> error "«script» doesn’t understand the “bar” message." number -1708 from «script»
tell bar to bar(42) --> error "«handler foo» doesn’t understand the “bar” message." number -1708 
end test

test()

To put it another way, the syntax “bar(42)” does not mean “look up the value of the property ‘bar’, whose value is a handler, then call the handler with the ‘42’ argument”; rather, it means “send a ‘bar(42)’ message”. You can see this in the Script Editor event log if you run the following script:

foo(42)

the event log is:

tell application "Script Editor"
foo(42)
--> error number -1708

As you can see, it sent an “invoke function ‘foo’ with argument ‘42’” message to the application, rather than sending a “get property foo” message to get a handler to invoke. It behaves the same way when sending the message to a script or any other object.

For example, you can use OSA to compile a handler without a surrounding script (without a “context”), then invoke it. You can even send it to another application process and execute it there.

Can't think why you'd want to send a bare «handler» instead of a complete «script».

The compiler can compile any bit of source text, including individual values like “42”, into a standalone object. You can compile a number or a string, a record, a list…or a handler. A script is just one of many types of values you can compile, as are handlers.

Reasons to do this include setting the value of a property via OSA, or comparing with the property's current value or with the result of running a script. Or to ask for its display string. Or to create a value that you then ask OSA to transform into an AEDesc. You can also produce a standalone value by asking OSA to pull it out of an AEDesc. This is what happens when OSAExecuteEvent sends a message to a script, or when AppleScript sends an Apple Event or receives a reply: values are encoded into AEDescs and decoded from them. (And any of these values can be returned from OSA as an OSAID.)

And bear in mind there are many things the OSA API lets you do that aren't actually valid operations…

Not the ones I described, so I don't understand why you mentioned that.


The real problem is that AppleScript's `script` objects lack a mechanism for making slots read-only, as that would allow slots containing handlers to be locked down so they cannot be moved or replaced. But AppleScript is full of these sorts of design defects and oversights, as most first attempts are…

That is incorrect: it is not a flaw, it is a simple matter of…simplicity. AppleScript is a lightweight dynamic, object-oriented language like Self and Smalltalk—neither of which has read-only properties. AppleScript is hardly alone in this. Many dynamic languages don't have read-only properties.

Nonsense. AppleScript already knows how to do read-only slots (e.g. the `parent` property)…

“Nonsense” is incorrect (or, at least, overstating the case). There is no facility for defining arbitrary read-only properties, is the point.

That's a good point about the parent property being read-only. But that's the only one. It's special. All programming languages have special cases. Any system for manipulating symbols, whether it's a programming language or a formal system, will either have special cases or be too trivial to be useful.

Whereas AppleScript «constant»s are not only constant, they're not even constants but are symbols instead; while supposedly 'constant' properties such as `pi` are eminently assignable, although I suppose `set pi to 3` will please at least Biblical literalists.

There are multiple meanings for “constant” in AppleScript documentation. One is for enumerations, which are constant values, like “current application” and “yes”. Another is for class identifiers that are used as values, like “missing value” and “January”. Yet another meaning is for global properties like “pi”, “return” and “quote”, which, although modifiable, are rarely modified by scripts. The documentation in the ASLG is attempting to describe all these in-practice-if-not-necessarily-in-theory read-only values with a common term so they can be discussed together as a set without a ton of jargon. If not “constant,” what term do you suggest for this concept?


If support for read-only properties is important to you, please file a request (hopefully with an example use-case): https://developer.apple.com/bug-reporting/

Honestly Chris, advocating deep technical enhancements to the core language is pointless.

Obviously, I disagree, and not just with words, but with my own actions:

I've been improving AppleScript every release for years, involving significant changes. I added libraries and use clauses, support for terminology in scripts, parameter type declarations and default arguments for handlers, “AppleScript/Objective-C everywhere”, and more. In particular, most of these are focused on making it possible to write libraries that can replace scripting additions, with a lot less code compared to writing event handlers in C.

To everyone else reading this, I'll reiterate: please file radars.

TL;DR: When a script uses `load script` to load libraries, those libraries are *owned* by that script only. When a script uses `script NAME` to load libraries, those libraries are *shared* by all scripts within the same CI. The latter creates a risk of very nasty, very non-obvious behavioral bugs occurring, because while it may be good practice for an app to create a new CI for every new, unrelated script it loads, this was never a documented requirement - or even a concern - for the first 20 years of AppleScript's life.

A given component instance can contain more than one script, and all the scripts share state like “text item delimiters”. This is a feature: you can have more than one script interact with state and with each other, executing one, then another, then the first one again, or what-have-you. Scripts are persistent, stateful objects that you send messages to, not just linear code that runs once, independently of all other scripts in the component instance, and component instances contain state shared by all the scripts within them. This has always been the case, and it has always been documented.

Moreover, the state inside the component instance isn't the only state scripts share. They also share the state of the application they run in, and of the objects they send events to. For example, one script can tell TextEdit to make a new document, and another can modify that document—and these scripts don't even have to be in the same component instance. If you consider it a risk to share state, then nearly every script has this risk. But in practice, it's the nature of scripting and it works fine.

Clients who wish to keep scripts more isolated from one another should place them in separate component instances. This has always been the case, and has always been documented (if not spelled out exactly in a way you recognized, of course). It is not a requirement, it is a capability available to clients who chose to make use of it. For example, I changed Script Editor to keep each script document in its own instance—because in a development environment like SE, the scripts are usually meant to be (more) independent of one another, often mean to run in separate applications. But it doesn't have to do that (it didn't for a long time)—it just seems to fit the use-case better.

But there is no such thing as a script that references or changes application state without sharing that state with other scripts. Any shared state within a component instance should be the least of your worries, if you consider it a risk. And I don't think it's something to “worry” about: it's just one of the many things to be aware of to write correct code.

Furthermore, this behaves just like Mach-O frameworks and plugins, and libraries in many languages. All the Mach-O code in a process shares the same global state with all the frameworks. Yet, unlike frameworks, OSA offers clients the flexibility of controlling where to place the boundaries between scripts, by arranging them in component instances however they see fit. i.e., because the AppleScript runtime is a virtual machine, you aren't restricted to the granularity of process boundaries; instead, you can have more fine-grained boundaries within a single process. This is a powerful feature, not a bug, and it is an option, not a requirement.

-- 
Chris Page
The other, other AppleScript Chris

 _______________________________________________
Do not post admin requests to the list. They will be ignored.
AppleScript-Users mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
Archives: http://lists.apple.com/archives/applescript-users

This email sent to email@hidden

  • Follow-Ups:
    • Re: AS Library Question
      • From: has <email@hidden>
    • Re: AS Library Question
      • From: has <email@hidden>
    • Re: AS Library Question
      • From: Shane Stanley <email@hidden>
    • Re: AS Library Question
      • From: Axel Luttgens <email@hidden>
    • Re: AS Library Question
      • From: Jean-Christophe Helary <email@hidden>
References: 
 >Re: AS Library Question (From: has <email@hidden>)
 >Re: AS Library Question (From: Chris Page <email@hidden>)
 >Re: AS Library Question (From: has <email@hidden>)

  • Prev by Date: Re: [ANN] CalendarLib, improved Calendar scripting
  • Next by Date: Re: AS Library Question
  • Previous by thread: Re: AS Library Question
  • Next by thread: Re: AS Library Question
  • Index(es):
    • Date
    • Thread