• 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: has <email@hidden>
  • Date: Thu, 17 Dec 2015 18:52:24 +0000

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. They can explicitly be displayed as “«handler foo»” and “handler” is a public class—you can ask for the class of a handler and get back “handler”, you can programmatically test whether an object is a handler, and you can invoke it by sending it a message. As in AppleScript, fetching the value of a property via OSA can return a handler, the result of executing a script can be a handler, and OSA's OSAExecuteEvent can be used to execute a handler.


Yeah, I'm aware of the OSA API's capabilities. However, I'm trying to keep it sufficiently simple for everyone else to understand. Plus, the OSA API still doesn't address the handlers-are-not-closures problem, which means that moving a handler using OSA calls will break static bindings just as surely as moving it using AS's `set` statement.


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', it sends a message to a [script] object, which checks to see if it has a handler with the same name, invoking that handler if it does or delegating the message to its parent object if it doesn't. AS message passing is like that of Smalltalk or Ruby, and not to be confused with the way that Python or _javascript_ do it, which is to get a bound function from an object and call it.

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, 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. (As I've said previously: undocumented behaviors do tend to be undocumented for a reason.:)

Also, as previously stated, `«handler foo»` isn't a closure, so this 'trick' is a disaster waiting to happen the moment `foo` contains any free variables or calls other handlers. Move the handler to another context, and Boom!!!

In other words, the mere fact that you can do this sort of thing does not mean you should. If the original AS team had intended for AS users to move handlers around as objects, they'd have made them take their original bindings with them. Besides, like I say, in a Smalltalk-y object system it doesn't really make sense anyway. Even Ruby, which is as cowboy as it comes, makes a clear distinction between methods, which are permanently bound to an object, and 'procs' that are created by asking an object to parcel a particular method into a portable format.


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». And bear in mind there are many things the OSA API lets you do that aren't actually valid operations: it's extremely loose and sloppy in its design, and even vaguer in its documentation. (Just look at all the sloppiness, vagueness, and confusion over which OSA APIs should or shouldn't be used when working with Script OSAIDs versus Value OSAIDs versus Context OSAIDs, which even the current AS team doesn't understand how to use correctly; as demonstrated by `osascript`'s embarrassingly broken interactive mode.)


Although this isn't spelled out, these are designed to work. If something here doesn't work, file a bug.

Certainly, if users have use-cases for working with handlers, filing a radar to make the necessary details publicly documented is encouraged: https://developer.apple.com/bug-reporting/

On the List of AppleScript Shit that Could Seriously Do with Being Sorted a Decade Ago, I would rank this about #10,001, which puts it well below "Redesign and Rewrite AppleScript from Scratch", and way, way, WAY below "Implement a Built-in Fricking `find/replace text` Handler Already". Honestly, don't do it. Just don't. AS is built around [script] objects which respond to messages, not callable 'function' objects. Stick to the clearly defined, documented, approved idioms, and avoid bouts of excessive cleverness that have a nasty tendency to come back on you.



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); it's just inconsistent in how it's applied. And I'm fairly sure Self and Smalltalk don't let you modify handler slots without jumping through serious hoops (and I would hope they're a lot more rigorous in not breaking scope bindings doing so). Self even knows how to do read-only data slots (i.e. a data storage slot that has a getter but not a setter), providing a robust, user-friendly way to define 'constants' that are actually *constant*.

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.


More importantly: although it could be convenient to be able to declare a read-only property to make your intent clearer and to see errors if you inadvertently try to write one, not being able to declare a property read-only doesn't prevent you from being able to write scripts that do what you want. There is no missing capability here, only a minor convenience for catching errors in your code.

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. AppleScript is a legacy language, like Tcl or Perl, and well into the long tail of its lifetime. Furthermore, it would just be an exercise in turd-polishing: AppleScript is fundamentally flawed in its design (even Dr Cook acknowledges this), and there is no way to fix those flaws short of throwing the whole thing out and rethinking, redesigning, and reimplementing everything from scratch, thereby breaking everyone's existing AppleScripts, of course.

The best ways in which you can improve *AppleScript* are through small, simple enhancements, such as a decent set of standard libraries. Yeah, I know corporate devs love a grand exciting complicated challenge over small simple BORING tweaks and improvements - I've fallen into that trap myself - but what will really thrill and improve the daily lives of jobbing AppleScripters are things like a robust, well documented standard library containing handlers for text, list, and date manipulation, and cleaner, simpler, more robust file reading and writing APIs (including full range of text file encoding options, and automatic closing of file handles after use).

And no, I'm not going to write a Radar ticket for this because, 1. I bogged off to languages that already give me all this stuff for free years ago, so AS's myriad failings don't affect me directly (short of Apple deciding to ditch AS - and thus AEs - altogether, in which case I'm totally boned too), 2. I'm all out of Xmas cheer after my most recent email exchange with Sal (so reckon it's someone else's turn to offer the next olive branch for a change), and 3. this stuff is so BASIC AND OBVIOUS that ANY conscientious developer who doesn't live their entire life in a perfect glass bubble should already be FULLY aware of where their product is failing its users both hardest AND in ways that could be very quickly, easily, and simply solved by himself in plenty time for the next OS release, even if he has to write his own Radar ticket just to keep the beancounters square.


> Similarly, if you use load script:
>
> set x to load script "/Users/shane/Desktop/Test.scpt"
> x's stripWhiteSpace
>
> And script libraries are really load script in new clothing.

That's technically incorrect: whereas `load script` creates new `script` objects within your current script (i.e. they become part of your own script's state), AS's official library mechanism loads scripts into the current Scripting Component Instance (CI) as new Script instances (OSAIDs) that live in parallel to your own scripts, and calls are passed from your scripts to library scripts using OSA's existing delegation system or [more likely] chicken voodoo.

Now who's relying on implementation details? ;-) Your apparently two different descriptions are in fact nearly identical, and there is no essential difference between scripts loaded with “load script” and scripts loaded via “script” element references, or, in fact, scripts defined in your source code.

Actually I'm relying on a decade of having to totally black-box reverse engineer the deepest, darkest recesses of AppleScript's inner workings[1], as Apple don't share any of that information with outsiders (and I suspect it'd be largely impenetrable even to insiders, assuming it exists at all).  :p

AFAIK, the `load script` handler does not store the de-serialized «script» object under a new, permanent OSAID, but instead simply returns it as its result. (It might use a temporary OSAID entry while reading and processing the script, but tons of OSA operations do that.) Whereas loading a library via a `script NAME` specifier does create a new OSAID entry in the CI's scripts table (or a similar internal table) in addition to returning it, thus acting as a reusable cache of all previously loaded library scripts. (e.g. Python and Ruby also do this, which is why they're an unsafe crapshoot for embedded use too.)

BTW, I hope you realize I'm just using 'OSAID' as convenient shorthand. I'm fully aware[2] it's just a numeric identifier that refers to a script object instance within whatever hash/array/tree/etc the AS CI uses to hold all script instances created via OSALoad/OSACompile. (Again, I'm trying to keep the hideous jargon vaguely within the realm of comprensibility, as the previous sentence attests.)

Of course, I *could* be wrong about the exact operation of `load script` versus `script NAME`, but I don't think I am. It's not mere idle hypothesis but *working* one, so would've had huge holes rapidly blown in it years ago, back when I was doing a lot more complex AE/OSA work. Obscure issues like data sharing between totally unrelated scripts that wouldn't touch most AS users tend to blow up pretty quickly when you start pushing this stuff as hard as I've done.


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.

STL;SDR: You write public APIs for the system you already have, not the system you *wish* you had.


has

[1] <https://en.wikipedia.org/wiki/Grue_(monster)>

[2] Anyone who's curious about what an OSA language component actually looks like inside (or thinks I don't know what I'm talking about:p), take a look at the [very rapidly written and rather messy but largely working] _javascript_ OSA component <http://sourceforge.net/projects/appscript/files> I put together last year while trying to explain/persuade/beg the AS team to implement JXA right. The fact my component still works better than their component, despite being hacked together by a total amateur with no skill, clue, or documentation worth a damn...
 _______________________________________________
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: Chris Page <email@hidden>
References: 
 >Re: AS Library Question (From: has <email@hidden>)
 >Re: AS Library Question (From: Chris Page <email@hidden>)

  • Prev by Date: Re: AS Library Question
  • 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