Whereas a value-returning function returns a single value, a shelf-class function is a function that returns an entire shelf.
A shelf-class function is defined by prefixing its return type by one of the three shelf class keywords: modifiable
, read-only
, or write-only
. For example,
define read-only integer function some-primes () as return { 2, 3, 5 with key "third", 7, 11, 13, 17 }defines a function
some-primes
that returns a read-only
shelf of integer
s, in this case a shelf literal that contains a few prime numbers. Since the
function was defined read-only
, the shelf's items will be readable at the call point, but not
modifiable. Only prefix functions can be shelf-class: infix-function
s and conversion-function
s
cannot. However, like any other prefix function, a shelf-class function can be both overloaded
and dynamic
/overriding
.
The result of a shelf-class function can be used like any other shelf, within the restrictions of the
function's class. For example some-primes
can be used in a repeat over
loop to output the first few prime numbers:
process repeat over some-primes () as p output "d" % p || "%n" againThe shelf returned from a shelf-class function can also be used in the header of a
using
scope, among other places. In addition, it can be indexed to extract a single item from the returned shelf:
process output "The second prime is: " || "d" % some-primes ()[2] || "%n"If keyed indexing is used, the key must exist on the referenced shelf:
process output "The third prime is: " || "d" % some-primes (){"third"} || "%n"A shelf-class function can also be passed as an argument to a function that takes a shelf-class argument: for example,
define string source function emit (read-only integer i) as repeat over i as i output "d" % i || "%n" again process output emit (some-primes ())However, since
some-primes
was defined as a read-only
shelf-class function, it
cannot be used as an argument to a function expecting either a modifiable
or write-only
shelf
class
argument:
define function wrong (modifiable integer i) elsewhere process wrong (some-primes ()) ; ERROR!
A shelf-class function returns a reference to an existing shelf. This can have surprising results. For example
the following shelf-class function returns a reference to a global
:
global string s initial { "Hello, World!", "Salut, Monde!" } define modifiable string function f () as return sIf the caller modifies the reference returned by the function
f
, the modifications are
reflected in the global
shelf s
:
process copy string { "Hola, Mundo!", "Halo, Welt!" } to f ()Following the execution of this
process
rule, the global
s
contains the two
items
Hola,
Mundo!
and Halo, Welt!
, rather than the initial Hello, World!
and Salut, Monde!
.
As indicated previously, the class of a shelf-class function impacts how the caller can use the shelf
reference returned from a shelf-class function. It also impacts which shelves a shelf-class function is allowed
to return. For example, a shelf-class function defined modifiable
cannot return a reference to a constant
, since the items of a shelf declared constant
are not modifiable. For the same reason, a
shelf-class function defined modifiable
cannot return a reference to one of its
read-only
arguments:
define modifiable string function f (read-only string s) as return s ; ERROR!
The rules for returning from a shelf-class function are:
local
or global
shelf of the
appropriate type, assuming the local
or global
is statically in-scope at the point of
return,
record
field
of the appropriate type,
assuming the record
that owns the field
is statically in-scope at the point of return,
read-only
can return a read-only
or modifiable
function argument of the appropriate type, or a constant
of the appropriate type,
modifiable
can return a modifiable
function argument of
the appropriate type, and
write-only
can return a
write-only
or modifiable
function argument of the appropriate type.
In this list, the term appropriate type is used rather than identical type, because the type of the shelf reference returned by a shelf-class function does not need to match the function's defined type identically: rather, the type of the shelf reference returned must be usable as the function's defined type, taking into account the class of the functions. The rules for the type of the shelf reference returned from a shelf-class function are:
read-only
shelf-class function must be the
same as, or any subtype of, the defined type of the function,
modifiable
shelf-class function must be
the same as the defined type of the function, and
write-only
shelf-class function must be
the same as, or any supertype of, the defined type of the function.
The shelf reference returned from a read-only
shelf-class function can be used only in a context that
does not modify the items of the shelf reference. For instance, a read-only
shelf-class function can be
used as a read-only
shelf-class argument to a function call or throw
, but not a modifiable
or write-only
shelf-class argument. A read-only
shelf-class function can also be
used on the left-hand side of the copy
action:
process local integer i variable copy some-primes () to isince
copy
does not modify its left-hand shelf reference. In this example, the read-only
restrictions on
some-primes
do not apply to the shelf i
, since these are two separate shelf references: they
simply contain items of the same value. Additionally, since some-primes
was defined read-only
,
the previous example cannot be written to use copy-clear
:
process local integer i variable copy-clear some-primes () to i ; ERROR!since
copy-clear
must modify (that is, clear) its left-hand shelf reference once the copy
has been performed.
The shelf reference returned from a write-only
shelf-class function can be used only in a context that
does not require the the items of the shelf reference to be readable. For instance, a write-only
shelf-class function can be used as a write-only
shelf-class argument to a function call or throw
, but not a modifiable
or read-only
shelf-class argument. A write-only
shelf-class function can also be used on the right-hand side of a copy
or copy-clear
action:
global string s variable define write-only string function absorber () as return s process local string t initial { "Hello, World!", "Salut, Monde!", "Hola, Mundo!" } copy t to absorber ()Here, the items of the
local
string
t
are copied to the shelf reference
returned by the function absorber
which is in turn a reference to the global
s
, so as a
result the
items of the
local
t
are copied to the global
s
.
The examples thus far have used parenthesized arguments when defining shelf-class functions. Like ordinary
prefix functions, shelf-class functions can use heralded arguments as well; for instance
define read-only string function casing of value string s as return { s, "ug" % s, "lg" % s }However, there is a subtlety involved that bears mentioning: indexers bind more tightly than heralded arguments. So when calling this function, if an indexer is to be applied to the result, parentheses are required:
process local string s initial { "Hello, World!", "Salut, Monde!", "Hola, Mundo!" } output casing of s[2] ; outputs "salut, monde!" output (casing of s)[2] ; outputs "HOLA, MUNDO!"
Shelf-class functions can be used to export objects from a module that are constant to the importer, but
modifiable by the module. A module simply has to export
a read-only
shelf-class function that
returns a global
which is itself not exported from the module:
module shared as "exporting shelves read-only" global string private-strings initial { "Hello, World!", "Salut, Monde!" } export read-only string function public-strings as return private-stringsSubsequently, an importer of this module can read the values of the shelf
private-strings
via a call to public-strings
, but since the latter is a read-only
shelf-class function, the importer cannot modify
the contents of the shelf, while the module itself retains the ability to modify the shelf's contents.