Scripting style: Verbs in function names
Mark Waddingham
mark at livecode.com
Fri Nov 4 05:57:01 EDT 2016
If the goal is to emulate (to some extent) the English-like nature of
script in our handler names, then I think it is instructive to look at
the syntactic forms employed in the current syntax that exists. I can
think of the following general forms:
- properties:
the X
the X of Y
- chunks (part access):
item X of Y
- predicates:
X is [ not ] <something>
there exists <something>
- functions (in the mathematical sense):
average(X, Y)
sin(Z)
- commands:
encrypt X with Y using password Z
import snapshot
A property is something which encapsulates a piece of state. Sometimes
they are constants; sometimes they are read-only and reflect state that
changes over time; sometimes they reflect something about the content of
something, or an attribute of something and only change when explicitly
set. Getting properties never have any side-effects; setting properties
only has side-effects within their domain of concern (whether it be a
library, or an object).
A chunk is something which allows access to a part of something. Again,
sometimes they are read-only, sometimes settable. Getting chunks never
have side-effects; setting chunks only have side-effects within their
domain of concern.
A predicate is a test which returns true or false, and is to test for a
particular condition. They never have side-effects.
A function (in the mathematical sense) takes inputs, and uses those
inputs to generate output. If we want to be completely pure, then they
should never have side-effects - they should be used for computational
devices... i.e. Things which synthesize a result based on input values
and related state.
A command is something which actually *does* something. Commands can and
do have side-effects.
I'd perhaps further suggest that evaluating functions, predicates,
chunks and properties should *not* be able to fail apart from due to
mis-use (e.g. asking for an object which doesn't exist, trying to
convert "ZOO" from base 10 to base 16). I think this is generally true
in the engine, the most prominent exception which comes to mind being
the 'url' chunk.
[ As a slight aside...
This latter sitation (functions being able to fail when it is not due to
mis-use) is why we have the odd situation with functions being able to
return two values (the result, and the actual return value). The reason
this is actually not ideal is that you can easily write code which is
not and cannot be made correct except by separating out onto multiple
lines. e.g.
processMyTwoBitsOfData url X, url Y
Here, if fetching url X fails, then the command will receive empty, and
still get executed which is technically wrong in pretty much any
scenario (after all, one of the url's could actually be empty by intent
and so you the receiving function cannot tell the difference between a
failure in fetching one of its arguments, and the argument actually
being empty). In order to write this correctly you need to do:
local tX, tY
put url X into tX
if the result is not empty then throw "fetching X failed"
put url Y into tY
if the result is not empty then throw "fetching Y failed"
processMyTwoBitsOfData tX, tY
Of course, this might just suggest that the url chunk should throw an
error in the case of failure; but that then violates the principal that
things you cannot predict failure of *must* be explicitly checked for
success. Unfortunately, however, the world is not perfect. ]
In contrast, commands (as they are performing an action, rather than a
computation) can and do fail in ways you cannot predict. This is
typically indicated by returning a non-empty result; with any actual
returned value from a command being placed in it (in the case the
command didn't fail). [ Note: Script now has the ability to set either
the result or it from a command handler - by using return for value, and
return for error ].
So, if the goal is to reflect English-like ness in our library function
naming (in lieu of being able to actually define our own syntax directly
in one of the above forms) then the following seem reasonable:
- property-like things: The<property>[Of<thing>]
- chunk-like things: <Chunk>Of<Thing> / TheNumberOf<Chunk>sOf<Thing>
- predicates: Is[Not]<Adjective> / ThereIs(A|An)<Noun>
- functions: <Noun> (with potential suffixes describing inputs)
- commands: <Verb> (with potential suffixes describing inputs)
I realize here function/command are a little too general - they are
difficult to abstract in a reasonable pattern as their naming will
largely depend on what they are doing.
The case in question was a MIME library with the following parts:
- a function which returns the version of MIME to use in the MIME
header - MIMEVersion
- a function which returns the mime type for a given extension -
MIMETypeForExtension
- a function which returns the list of extensions matching a
wildcarded mime pattern - MIMETypeExtensions
- a command which encodes the input in a MIME transfer encoding -
MIMEEncode
- a command which creates a MIME multipart document from an array of
pieces - MIMECreateMultipartContent
- a command which creates a MIME multipart document from a field -
MIMECreateMultipartContentFromField
- a command which creates a MIME multipart email from a pre-encoding
multipart document and attachments - MIMECreateEmail
A naming scheme consistent with the above 'english-like' forms could be
something along these lines:
The MIMEVersion is actually a read-only property:
syntax: the MIMEVersion
function form: mimeTheMIMEVersion
The MIMETypeForExtension is essentially a parameterized read-only
property, or chunk:
syntax: the MIMEType of extension <ext>
function form: mimeTheMIMETypeOfExtension(<extension>)
The MIMETypeExtensions as currently posed is either a direct constant
lookup, or a 'map' over a list of constants. I think it is reasonable in
this case to consider a 'MIMEType' as either being a fixed string, or
wild-carded string (at least restricted wild-carded - e.g. text/* but
not t*t/plain):
syntax: the extensions of MIMEType <mimetype>
function form: mimeTheExtensionsOfMIMEType(<mimetype>)
THe MIMEEncode command is actually a pure function - it takes an input
and produces an output entirely based on the input, it cannot fail:
syntax: <data> encoded for <transfer-encoding> MIME transfer
function form: mimeEncodeForMIMETransfer(<data>,
<transfer-encoding>)
('for' suggested here because the transformation is *for* a purpose,
not an end state)
The MIMECreateMultipartContent is again a pure function - it encodes a
sequence of transfer-encoded parts into a multipart document:
syntax: <parts> encoded as MIME multipart document <type> [ with
<params> ]
function form: mimeEncodeAsMIMEMultipartDocument(<parts>,
<multi-part type>, <params>)
('as' suggested here because the transformation produces something
which is an end state - a multipart document)
The MIMECreateMultipartContentFromField, again a pure function - it
encodes the content of a field as a mime multipart document:
syntax: <field chunk> encoded for MIME multipart document
function form: mimeEncodeFieldAsMIMEMultipartDocument(<field id>)
The MIMECreateEmail is not truly a pure function as it can fail - it has
to potentially load data from files in the attachment arrays. It also
has quite a long argument list so is probably better considered a
command:
syntax: encode <body> as MIME email with subject <subject> from
<sender> to <recipient> [cc <cc>] attaching <attachments> [ into
<container> ]
command form: mimeEncodeAsMIMEEmail pBody, pSubject, pSender,
pRecipient, pCC, pAttachments
Are these names any better than the ones already used in the library? It
is hard to say - naming preferences are highly subjective!
However, one thing which I think is useful to consider in mind is that
difficulty in naming things can (in many cases) indicate that the design
is of the API is perhaps not quite right. I did the above reimagining of
the current names in the library without considering whether there was a
better way to 'slice and dice' the functionality. (For example, would it
be better to have the concept of a 'mime email object' which you use
setters and getters to configure?).
Just my two pence.
Mark.
--
Mark Waddingham ~ mark at livecode.com ~ http://www.livecode.com/
LiveCode: Everyone can create apps
More information about the use-livecode
mailing list