This chapter describes how to define ScriptX functions and
also discusses the ScriptX thread and pipe operators.
Defining Functions
Functions, like all things in ScriptX, are objects (literally,
instances of a subclass of AbstractFunction
,
typically ByteCodeMethod
). There are two ways to
define functions. A named function definition creates a new
variable and immediately assigns the function object to it. An
anonymous function is not assigned to a variable, although it
can be assigned in a subsequent expression. Anonymous
functions are described on page
101.
If you are defining a recursive function, you must use a named
function. This is true because a named function is the only
kind of implicit or explicit variable declaration in which you
can use a name within the initializing expression and have it
refer to the variable being defined. In other cases, it refers
to an outer scope variable of that name.
Function Syntax Summary
function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \
The
-> body
function
reserved word, which can be
shortened to simply fn
, signals that this is the
start of a function definition, and fnName is the
name of the variable this function object is assigned to.
After the function name are the parameters to the function:
positionalArgs specify arguments that are required by
the function and must be specified in a specific order
(position), restArg holds an array of optional
arguments given when the function is called, and
keywordArgs is a list of keyword arguments. All three
types of arguments are optional, although they must be
specified in this order. Function Names and Variables
local
. Note that
just as with local variables, you can only define a local
function inside a local scope (that is, not at the top
level).local function sortIt theList -> . . .
constant
in the
function definition, which means that you cannot assign
anything else to it once the function has been created (except
by redefining another function). This is to prevent
accidentally overwriting the definition of a function with a
simple assignment. If you expect to reuse the global variable
name to which you've assigned a function elsewhere in your
script, consider using the anonymous function construct
instead (or a local variable, if appropriate). Functions with Positional Arguments
function square n -> n * n
square 10
100
function beepMe -> print "beep!"
beepMe()
"beep!"
If a function has no arguments, empty parentheses are
required.
Functions with Keyword Arguments
#key
keyword in the function definition, followed
by the definitions of the keywords themselves:function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \
Keyword arguments are optional in the function definition, but
if they are included, they must be specified after both the
positional arguments and the optional "rest" argument
(described in the next section).
-> body
#key
reserved word. Keyword arguments look like
this:keyName:[ argName ] [ ( initialValue ) ] . . .
where:
Here are some examples:
-- reportArgs simply prints out its a and b values
function reportArgs #key a: b: -> (print a; print b)
reportArgs a:10 b:20
10
20
reportArgs a:100
100
unsupplied
reportArgs a:10 b:30 a:50
10
30
-- reportArgs2 is the same as reportArgs, but the values of the
-- keys are assigned to the local variables moo and quack -- instead of a and b.
function reportArgs2 #key a:moo b:quack -> (
print moo; print quack
)
reportArgs2 a:10
10
unsupplied
-- reportArgs3 defines default values for a and b so -- neither will appear as unsupplied
function reportArgs3 #key a:(1) b:(2) -> (
print a; print b
)
reportArgs3() -- use all defaults
1
2
reportArgs3 a:20
20
2
-- reportArgs4 defines default values -- and assigns them to local variables
function reportArgs4 #key a:moo(1) b:quack(2) -> (
print moo; print quack
)
reportArgs4()
1
2
reportArgs4 a:6
6
2
The
#rest
argument allows you to define a
function with a variable number of arguments. In this sense,
"rest" means "the rest of the arguments after the positional
arguments, except the denoted keyword arguments." The function
collects those "rest" values into an array, and in the body of
the function you can access elements in that array. Unlike positional arguments, rest arguments are not required-when you call the function you can supply no rest arguments, or as many as you need to.
Notice in the following function definition that
#rest
takes a single argument restArg.
The combination #rest
restArg comes
after any positional arguments and before any keyword
arguments. By convention, the argument args
is
typically used for restArg (though you could name it
whatever you want), so what you often see in examples is the
combination #rest
args
.
function fnName [ positionalArgs ] [ #rest restArg ] [ #key keywordArgs ] \This is an example of a function that takes no arguments other than its rest arguments:
-> body
-- addEmUp takes a variable number of arguments, sums them all -- and returns the sum
function addEmUp #rest args -> (
local sumArgs := 0
for i in args do sumArgs := sumArgs + i
)
addEmUp 1 2 3 4 5
15
addEmUp 3 4
7
In place of using
args
in the previous example,
we could have used numList
, to denote the list of
numbers.This example defines a function that takes a single required argument and a variable number of rest arguments (each argument happens to itself be an array):
-- joinArray takes a single required array and a variable number -- of other arrays. It builds a single array of all the -- elements in all its arguments
function joinArray array1 #rest otherArrays -> (
for i in otherArrays do addMany array1 i
return array1
)
joinArray #(1,2,3) #(4,5,6)
#(1, 2, 3, 4, 5, 6)
joinArray #() #(@angora,@persian) #(@egyptianmau) #(@housecat)
#(@angora, @persian, @egyptianmau, @housecat)
If your function definition uses both the #rest
and #key
reserved words to define both rest and
keyword arguments, the non-positional arguments can only be in
the form of keyword-value pairs
(keyword:value). You cannot mix arbitrary
numbers of #rest
arguments and keyword
arguments.
When a function with both rest and keyword arguments is called, the keywords and values supplied to the function are stored into the rest argument in sequence (key, value, key, value, and so on) with the keywords appearing as
NameClass
objects.
function showRest #rest args #key a: b: c:(100) ->
print args debug
showRest a:10 b:20 c:30
#(@a, 10, @b, 20, @c, 30)
showRest a:10
#(@a, 10)
showRest()
#()
Note that the compiler translates a keyword argument into a pair of conventional arguments. The following function calls are equivalent:
grok foo:10 moof:20
grok @foo 10 @moof 20
return
block control expression:
return expressionWhen a
return
expression is encountered in the
body of a function definition, the function is immediately
exited with the value specified by expression. Functions that do not specify a specific return value return the value of the last expression evaluated while executing the function.
function factorial n -> (
if n <= 0 then return 1
else return (n * (factorial (n - 1)))
)
-- now, try a few test values
factorial 0
1
factorial 4
24
function sumAndPrint #rest args -> (
local mySum := 0
for i in args do mySum := mySum + i
format debug "The sum is: %*\n" mySum @normal
return mySum
)
sumAndPrint 10 20 34 45
The sum is: 109
109
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.