On 18/12/2015 11:16, Chris Page wrote:
TL;DR: Handlers are objects, just like any other
AppleScript value—also, file radars.
Handlers are NOT "objects just like any other", because that implies
they can be [re]assigned and used freely just like any other. Which
is not true.
For it to be true, AS handlers would need to be implemented as
*closures* - i.e. a first-class function object that captures the
scope in which it was defined. Which they clearly are not, as moving
them to another scope causes all of their dynamic bindings to break,
and all of their static bindings to REALLY break.
Whereas, AppleScript's *script objects* are closures - or at least
objects that implement closure-style behavior. That's from the
horse's mouth, BTW: from Dr Cook via Matt N the last time I said AS
didn't have closures; it does, it just does them in a way that
captures the spirit (and state:) of closures rather than following
their strictly textbook definition (which is a _function_ object
that captures scope). Users can even confirm this for themselves:
on foo()
local x
set x to 1
script A
on doit()
set x to x + 20
return x
end doit
end script
script B
on doit()
return x
end doit
end script
{A, B}
end foo
set {A, B} to foo()
{doit() of A, doit() of B} --> {21,21}
The local variable `x` is defined in the foo function's current
activation record (scope). Scripts A and B capture this *scope*,
retaining it even after `foo` has returned. Thus, when script A
modifies the value of `x`, `x`'s new value is also seen within
script B. That can only happen if the variable itself is captured,
not merely its original value. (Were it otherwise, the result would
be {21,1}.)
The fact that handlers are implemented in a way that allows them
resemble other values is an internal detail: it avoids script
objects having to implement two separate slot mechanisms - one for
storing handlers and one for storing everything else - with all the
added complexity that would create. But the fact their evaluation
APIs are polymorphic at the C++ level does not make any resulting
value-like behaviors one might observe within AS code a *language
feature*. Meanwhile, it's totally obvious from the way they behave
on the AS side that they were only designed to be called within
their original context, where its bindings are fixed and known to
it, and definitely NOT designed to be moved to other, totally
unknown contexts. Just as it's obvious that `set foo to bar; bar()
of foo` is nonsensical *AppleScript code*; and the fact it produces
a result just an accident resulting from the way those C++ APIs are
implemented Lastly, if it's not described in the public
documentation, it's NOT a public feature, and anything that results
from trying to use it is entirely accidental and unintended, and on
the head of the foolhardy user who ignores that documentation be it.
You're a C man, so you should know better than to advocate the use
of **undefined behaviors**, _especially_ to an audience that lacks
the deep CS education to understand what an incredibly _bad_ idea
that is. Not only should you have a duty of care to your users, you
should be obligated not to be grossly irresponsible. While a few
folk like me might know enough to go "WTF?!!", most will simply
accept what you say as exciting new knowledge to play with.
..
Y'know, I'd be happy to speculate idly that maybe the original AS
team had a TODO on their list to make `set bar to someHandler` work
as a convenient shorthand for saying "capture the closure (script
object) within which someHandler is defined"; and that's why handler
slots were never explicitly locked. Apple shitcanned the original
team after v1.1 came out, causing its designers quit in protest
before the language ever had a chance to mature, so it's possible
lots of loose ends were left dangling them. But honestly, I think it
very, very unlikely, and in any case such disconnected daydreaming
is precisely bugger all use to actual working AppleScript users.
From studying the way AppleScript was originally designed, now
operates, and how it compares to similar object messaging systems
like Smalltalk's where you don't do this sort of thing either, it's
clear that the right way to pass "handlers" around scripts is to
pass around the script objects containing those handlers. That is
what the documentation describes, and that is what you should be
advocating too. Anything else is just creating confusion and
complexity and misdirection for users, and feeding them just the
rope they need to hang themselves.
A well-designed language has _idioms_, standard ways of performing
tasks that are clearly defined and limited in number, with minimal
overlap or redundancy. Design is not just what a language can *do*,
but how it is *learned*. In end-user programming in particular,
learnability is more important than anything else, even if it means
leaving out cool but non-essential bells and whistles that more
programmer-indulgent languages would happily sling in. And the
biggest idiom in AppleScript is "objects", and the second biggest is
"commands", and the result of combining those two ideas is that
commands are instructions you "send"[1] to objects to get stuff
done.
The notion of first-class function objects fits nowhere with either
of those, just as it doesn't fit into Smalltalk, which is one of the
benchmarks for laser-focused parsimonious language design. It's only
when you get to high-functioning-idiot-ware like C++ that
complexity-junkie language designers toss all responsibility to the
wind in a pathological attempt to squeeze in every single kitchen
sink in existence; but just because one _can_ do anything does not
mean one _should_ - as any poor bastard who's ever tried to learn
the *entire* C++ specification will tell you. (Still, at least
designers like K&R and Stroustrup never went into automobiles,
or else cars would come with five different steering wheels and
explode every few dozen miles.)
So on balance of probability and hard evidence, I am completely
confident the AS designers *never* intended anyone to do what you're
telling them they can do, and anything it does do is entirely
unintended and *definitely* doesn't work for any meaningful
definition of "won't explode in your face because DON'T DO THAT". If
you can pony up internal development documentation or original code
comments that prove otherwise, or even wheel in Dr Cook to tell us
himself, I'll be happy to revise that. Otherwise I stand by my
assertion that you've lost perspective on the issue and are just
good old-fashioned wrong.
Regards,
has
[1] To clarify the scare-quotes: In AS itself, you send messages to
script objects, in traditional OOP fashion, e.g. `foo() of bar of
baz` sends message `foo` to the object currently stored in the `baz`
object's `bar` property. When sending messages to scriptable
applications though, appearances get deceptive due to AS's
obfuscated syntax. In fact, you aren't sending messages directly to
document/folder/track/etc. objects, but rather you're always sending
messages to the `application` object, passing query objects that
identify the object(s) you want to manipulate as parameters to that
message. Just in case anyone is lured into the massive misconception
that Apple event IPC is OOP, it isn't; it's RPC plus simple
relational queries.
|