Arguments

This chapter describes arguments to function calls in the Curl® language. In particular, it describes the following:
The information in this section applies to global procedures, anonymous procedures, class procedures, methods, constructors, and factories, which we will refer to collectively as functions, although the examples shown are all of global procedures, for simplicity. The concepts described in this chapter also apply to text formats and text procedures. However, the arguments to text formats and text procedures have a different syntax.

Introduction to Arguments

Summary:
  • Two categories: listed arguments and rest arguments.
  • Two formats: positional arguments and keyword arguments.
Arguments allow you to pass information to functions. There are two categories of arguments:
These categories of arguments can include arguments passed in either or both of the following formats:

Using Positional Arguments

Summary:
  • A caller must supply a value for each positional argument.
  • A caller must supply values for positional arguments in the correct order.
A positional argument has the following syntax:
arg-name[:arg-type]
where:
Examples of positional argument definitions follow:
|| A procedure that takes one integer positional
|| argument (i)
{define-proc public {my-proc1 i:int}:void
    || Body of procedure
}

|| A procedure that takes one "any" positional
|| argument (a)
{define-proc public {my-proc2 a}:void
    || Body of procedure
}

|| A procedure that takes two positional arguments:
|| a bool (b) and a char (c)
{define-proc public {my-proc3 b:bool, c:char}:void
    || Body of procedure
}
The number, data type, and order of positional arguments in a function call must match those of the function definition. To supply a value for a positional argument, supply the value in the call to the function. If you are supplying more than one argument in a function call, use commas to separate the arguments. For example, all of these are legal procedure calls:
{my-proc1 13}
{my-proc2 13}
{my-proc2 'a'}
{my-proc2 "Hello"}
{my-proc2 true}
{my-proc3 true, 'a'}

Using Keyword Arguments

Summary:
  • A caller need not supply a value for each keyword argument.
  • Each keyword argument definition includes a default value.
  • A caller can supply values for keyword arguments in any order.
A keyword argument has the following syntax:
arg-name[:arg-type]=arg-value
where:
Examples of keyword argument definitions follow:
|| A procedure that takes one integer keyword
|| argument (i)
{define-proc public {my-proc4 i:int=0}:void
    || Body of procedure
}

|| A procedure that takes two keyword arguments:
|| a bool (b) and a char (c)
{define-proc public {my-proc5 b:bool=true, c:char='a'}:void
    || Body of procedure
}
If the signature of a function has keyword arguments, you need not supply values for those arguments when calling the function. If you do not supply a value for a keyword argument, the function uses the default value (arg-value). To pass a value for a keyword argument, assign the value to the name of the argument in the call to the function. Because you are supplying the name of the argument in the function call, you can supply keyword arguments in any order. In the case of the my-proc5 procedure defined above, this means each of the following is a valid procedure call:
{my-proc5}
{my-proc5 b=true}
{my-proc5 c='q'}
{my-proc5 b=true, c='q'}
{my-proc5 c='q', b=true}
If a keyword argument is supplied multiple times, the last-supplied instance of the keyword argument is used for the argument binding. All other values are evaluated, type checked, and discarded.
For example:

Example: Using Keyword Arguments
|| A procedure that returns the sum of two numbers.
{define-proc public {add-two-numbers number1:int=12, number2:int=13}:int
    {return number1 + number2}}

|| Call the procedure, supplying the arguments.
{add-two-numbers}
{add-two-numbers number1=12}
{add-two-numbers number2=13}
{add-two-numbers number1=12, number2=13}
{add-two-numbers number2=13, number1=12}
{add-two-numbers number2=69, number1=12, number2=13}
The following example illustrates the initial binding of keyword arguments to uninitialized-value-for-type. The example attempts to set default values for the keyword arguments using global variables that have the same names. This attempt fails, because the global variable names are shadowed by the keyword names, which are bound to the uninitialized-value-for-type, which is 0 for these integers.

Example: Show Initial Keyword Binding
{let number1:int = 12}
{let number2:int = 13}
|| A procedure that returns the sum of two numbers.
{define-proc public {add-two-numbers 
                        number1:int = number1, 
                        number2:int = number2}:int
    {return number1 + number2}}

{add-two-numbers}

Using Both Positional and Keyword Arguments

Summary:
  • In the function definition, you can specify positional and keyword arguments in any order.
  • In a function call, supply the positional arguments in the same order as in the function definition, but supply the keyword arguments in any order.
To define a function with both positional and keyword arguments, you can specify positional and keyword arguments in any order. For example, in the following code, the signature of the add-two-numbers procedure includes one positional argument (number1) and one keyword argument (number2):
{define-proc public {add-two-numbers number1:int, number2:int=13}:int
    {return number1 + number2}
}
To call a function with both positional and keyword arguments, you can supply the keyword arguments in any position and in any order. However, you must supply the positional arguments in the same order as in the function definition. You can include keyword arguments among the positional arguments. However, you must maintain the relative order of positional arguments to one another. The following example shows some of the different ways that you can call the procedure defined above:

Example: Using Keyword Arguments and Positional Arguments
|| A procedure that returns the sum of two numbers.
{define-proc public {add-two-numbers number1:int, number2:int=13}:int
    {return number1 + number2}
}

|| Call the procedure, supplying the arguments.
{add-two-numbers 12, number2=13}
{add-two-numbers number2=13, 12}
{add-two-numbers number2=69, 12, number2=13}

Listed Arguments

Summary:
  • All arguments appear in the function definition.
  • Arguments can have the positional or keyword format.
Listed arguments appear in the function definition. Listed arguments can take the form of positional arguments or keyword arguments. For example, the following procedure definition has two listed arguments (arg1 and arg2). Both of the listed arguments are positional arguments.
{define-proc {my-proc arg1:int, arg2:int}:bool
    {return arg1 == arg2}
}
Also, the following procedure definition has two listed arguments (arg1 and arg2). In this case, both of the listed arguments are keyword arguments.
{define-proc {my-proc arg1:int=13, arg2:int=19}:bool
    {return arg1 == arg2}
}
And finally, the following procedure definition has three listed arguments (arg1, arg2, and arg3). In this case, two of the listed arguments are positional arguments and one is a keyword argument.
{define-proc {my-proc arg1:int, arg2:bool, arg3:int=19}:bool
    {return (arg1 == arg3) and arg2}
}

Rest Arguments

Summary:
  • Three periods (...) at the end of the list of arguments indicate that a function accepts rest arguments.
  • Arguments can have the positional or keyword format.
  • If a keyword argument is supplied multiple times, the function uses the last-supplied argument.
  • You can optionally specify a data type for the rest arguments. However, if you specify a data type, you cannot have keyword arguments.
  • Use a special form of the for loop to access the arguments in the rest arguments container.
Rest arguments allow functions to accept an unlimited number of unnamed arguments. Rest arguments are not individually named in the function definition. Instead, three periods (...) at the end of the list of arguments in the function definition indicate that a function accepts rest arguments.
If (...) appears in the definition of the function, any number of rest arguments can be supplied when calling the function. If you want, you can supply no arguments. Like listed arguments, rest arguments can take the form of positional arguments or keyword arguments.
Rest arguments are actually stored in a rest arguments container, with ... representing the container. All rest arguments are stored in the container in the order in which they are encountered. You can then iterate over the container to access the arguments. Duplicate keyword arguments are not merged in the rest arguments container. This means that if a keyword argument is supplied multiple times, the rest argument mechanism stores all occurrences.
You can optionally specify the data type for rest arguments. If you specify the data type, then the rest arguments cannot include keyword arguments and each argument must have the specified data type. For example, the following code defines a set of rest arguments, each of which has the String data type:
...:String

Specifying Rest Arguments in a Function Definition

To specify rest arguments, include three periods (...) at the end of the list of arguments. For example:
|| A procedure that takes rest arguments.
{define-proc public {my-proc6 ...}:void
    || Body of procedure
}

|| A procedure that takes two keyword arguments:
|| a bool (b) and a char (c) and rest arguments.
{define-proc public {my-proc7 b:bool=true, c:char='a', ...}:void
    || Body of procedure
}
It is also possible to specify a data-type for the rest arguments. This can be very useful. For example:
|| A procedure that can take any number of int arguments
{define-proc public {my-int-proc ...:int}:void
    || Body of procedure
}

Working with Rest Arguments

To access the arguments in a rest arguments container, use a special form of the for loop. The syntax of this form of the for loop follows:
{for (val-identifier, key-identifier) [key loop-counter] in ... do
statements
}
where:
If the rest arguments supplied by the caller do not include keyword arguments, use the following syntax:
{for val-identifier [key loop-counter] in ... do
statements
}
When the runtime encounters the for loop, it iterates through the rest arguments container, executing statements for each argument. For example:

Example: Working with Rest Arguments
|| A procedure that takes rest arguments and returns a StringBuf.
{define-proc {my-proc ...}:StringBuf
    || A variable to hold the return value of the procedure.
    let result:StringBuf = {StringBuf}
    || For each value in the rest arguments container, concatenate
    || the value to the string and append a space to the end
    || of the string.
    || Note that this loop will generate a runtime error if the
    || rest arguments container has a keyword argument (or, in
    || fact, any argument that is not a StringInterface, which is
    || the base class for strings in the Curl language).
    {for v in ... do
        {result.concat v}
        {result.append ' '}
    }
    || Return the string
    {return result}
}

{value
    || Some strings.
    let s1:String = "Here"
    let s2:String = "comes"
    let s3:String = "Curl!"

    || Call the procedure, supplying the strings as rest arguments.
    {my-proc s1, s2, s3}
}
Remember that you can pass a rest arguments container as an argument in another function call. For example:

Example: Passing a Rest Argument Container as an Argument
|| A procedure that takes rest arguments and returns a VBox.
{define-proc {my-vbox ...}:VBox
    || Return a call to VBox with the rest arguments container
    || as the first argument.  Because the "background"
    || and "spacing" arguments appear after the rest
    || arguments container, these settings will always take
    || effect (even if the same keyword argument appears
    || in the rest arguments container).  You can use this
    || technique to ensure that default settings are applied,
    || even if other values are specified as rest arguments.
    {return {VBox ..., background="pink", spacing=5pt}}
}

{value
    || Call the procedure, supplying the following rest arguments:
    ||  1) a keyword argument indicating the background color of
    ||     the VBox
    ||  2) a keyword argument indicating the alignment in the VBox
    ||  3) a String to place in the VBox
    ||  4) a Button to place in the VBox
    ||  5) a String to place in the VBox
    ||  6) a keyword argument indicating the margin of the VBox
    {my-vbox
        background="beige",
        halign="center",
        "Hello World",
        {CommandButton label="Click Me!"},
        "Goodbye",
        margin=5pt
    }
}

Documenting Rest Arguments

Since rest arguments are not reflected in the definition of a function, you should explicitly document the intended contents of rest arguments. This will help to ensure that those calling the function supply the correct rest arguments. When documenting rest arguments, you should specify the following:

Specifying Rest Arguments in a Call

If you call a function that takes rest arguments, check the documentation for the function to determine which arguments you should supply. According to the syntax of the Curl language, supplying rest arguments is optional.

Rest Arguments and Performance

Summary:
  • If you are passing a rest arguments container as an argument to a function call, make sure that the function being called takes rest arguments.
  • If you are calling a function that takes both positional arguments and rest arguments, try to supply the number of positional arguments that the function is expecting.
  • Avoid the use of keyword arguments in rest arguments containers that you pass as arguments in function calls.
Working with arguments to function calls requires system resources at compile time and at run time. In most cases, these resource requirements are not significant. However, you might want to take care to avoid certain uses of rest arguments that might result in excessive demands on the system during execution of a program.
If you are passing rest arguments containers as arguments to function calls, make sure that the functions being called take rest arguments. Also, try to make sure that the last argument in the function call is the rest arguments container and that the last argument in the argument list of the function that you are calling is rest arguments.
Also, if you are calling a function that takes both positional arguments and rest arguments, try to supply the number of positional arguments that the function is expecting. If you do this, it is not necessary for a new rest arguments container to be created. For example, in the following code the call to foo in bar results in more efficient operation than the call to foo in baz:
|| A procedure that takes one positional argument and
|| rest arguments.
{define-proc {foo x:int, ...}:void
    || Body of procedure.
}

|| Efficient use of rest arguments: the call to "foo"
|| has one positional argument and the rest arguments
|| container.
{define-proc {bar ...}:void
    {foo 37, ...}
}

|| Inefficient use of rest arguments: the call to "foo"
|| has a positional argument, another positional
|| argument, and the rest arguments container.
{define-proc {baz ...}:void
    {foo 37, 94, ...}
}
Try to avoid the use of keyword arguments in rest argument containers that you pass as arguments in function calls. They result in very inefficient operation.

Arguments Class

Summary:
  • The Curl language uses the Arguments class to handle arguments to function calls.
  • Like a rest arguments container, an Arguments object is a container that holds the arguments.
The Curl language uses the Arguments class to treat argument containers as first-class values. You can use the Arguments class to create Arguments objects. An Arguments object is actually a container that holds the arguments (in much the same way as the rest arguments container is a container that holds arguments). For example, the following code defines an Arguments object and initializes it with a number of positional and keyword arguments:
let args:Arguments = {Arguments background = "beige",
                                color = "blue",
                                "Here...",
                                "comes..."}
You can use the fields and methods of the Arguments class to work with the arguments in the container. For more information, see the description of Arguments in the API Reference. To pass the arguments in an Arguments container as arguments to a function call, you can use the splice expression, which is described in the next section of this chapter.
As with rest argument containers, you can use a special form of the for loop to access the arguments in an Arguments object. The syntax of this special form of the for loop follows:
{for (val-identifier, key-identifier) [key loop-counter] in arg-object do
statements
}
where:
If an Arguments object might contain keyword arguments, make sure to include the key key-identifier syntax. If you do not, a runtime error will be thrown.
When the runtime encounters the for loop, it iterates through the elements of the Arguments object in sequential order, executing statements for each argument.

Using splice

Summary:
  • splice takes a container and returns the elements of the container as a list of arguments.
  • You can use splice with various containers including arrays, strings, queues, sets, hash tables, Arguments objects, and rest argument containers.
  • Because the compiler simply replaces the splice expression with the elements of the container in the form of a list of arguments, you can treat a splice expression as a list of arguments.
The Curl language has a special expression called splice that takes a container and inserts its elements into a list of arguments. This allows you to easily supply the elements of a container as arguments to a function call. For example, you can use the splice expression to include all the elements of an array as arguments to a function call. You can also use the splice expression to pass the contents of containers like an Arguments object to a function. You can use splice with any container that a for loop can iterate over. Such containers include arrays, strings, queues, sets, hash tables, Arguments objects, and rest argument containers. Because the compiler simply replaces the splice expression with the elements of the container in the form of a list of arguments, you can treat a splice expression as a list of arguments.
This is a very powerful feature that is not a part of other programming languages, such as C, C++, and Java™. When you compile source code that includes a splice expression, the compiler replaces the splice expression with the elements of the container in the form of a list of arguments. However, note that you can use the splice expression only in the context of a list of parameters. You cannot use it as a general-purpose way to access the elements of a container.
The splice expression has the following syntax:
{splice container-name}
where container-name is the name of the container. You can use splice with most containers in the Curl language, including arrays, strings, queues, sets, hash tables, Arguments objects, and rest argument containers. For example, to call splice for an Array called my-array, use the following syntax:
{splice my-array}
A splice expression can appear only in the context of a list of arguments to a function call. So the example above, in its proper context, might look something like:
{my-proc {splice my-array}}
Because the compiler simply replaces the splice expression with the elements of the container in the form of a list of arguments, you can treat a splice expression as a list of arguments. In other words, you can include a splice expression at any position in the list of arguments. For example, all of the following uses of the splice expression are valid:
{my-proc {splice my-array}}
{my-proc {splice my-array}, arg-n}
{my-proc arg-1, arg-2, {splice my-array}}
{my-proc arg-1, arg-2, {splice my-array}, arg-n}
You can use the splice expression with the two most common containers for arguments to function calls: a rest arguments container (...) and an Arguments object. When you use the splice expression with one of these argument containers, it returns both the positional and keyword arguments, preserving the order of the arguments. However, using splice with a rest arguments container is redundant; the rest arguments container returns the arguments in the same format. The following example shows the use of splice with an Arguments object that contains both positional and keyword arguments:

Example: Using splice with an Arguments Object
{value
    || Define an Arguments container and initialize it with
    || a number of positional and keyword arguments.
    let args:Arguments = {Arguments background = "beige",
                                    color = "blue",
                                    "Here...",
                                    "comes..."}

    || Define a VBox and initialize it with the return value
    || from a call to the spaced-vbox procedure.
    || A "splice" expression includes the arguments from the
    || Arguments object initialized above.
    let vb:VBox = {spaced-vbox
                      "Hello world!",
                      {splice args},
                      "Curl!"}

    || Display the VBox
    vb
}
When used with containers that are not designed specifically to hold arguments, the splice expression returns the elements as positional arguments. This is the case, even if the container supports the keyword syntax and includes keywords. If the splice expression encounters a keyword in a container that is not designed specifically to hold arguments, it ignores the keyword.
The following example shows the use of splice with an array:

Example: Using splice with an Array
{value
    || Define an array of strings and initialize it with
    || three strings.
    let some-strings:StringArray =
        {new StringArray, "Here...", "comes...", "Curl!"}

    || Define a VBox and initialize it with the return value
    || from a call to the spaced-vbox procedure.
    || A "splice" expression includes the elements of the
    || array initialized above as arguments in the procedure call.
    let vb:VBox = {spaced-vbox
                      "Hello world!",
                      {splice some-strings}}

    || Display the VBox.
    vb
}
To further illustrate the use of the splice expression, consider the problem of concatenating the contents of an array of Strings. You can create a StringBuf to hold the intermediate contents of the string. Then, you can iterate through the elements of the array, concatenating each String to the StringBuf. And finally, you can convert the StringBuf to a String. However, by using the splice expression, you can simply supply the elements of the array as arguments to a call to String. For example:

Example: Using splice with Strings
{value
    || Define an array of strings and initialize it with
    || three strings.
    let some-strings:StringArray =
        {new StringArray, "Here...", "comes...", "Curl!"}

    || Define a String and initialize it with the return value
    || from a call to splice.  The "splice" expression includes
    || the elements of the array defined above.
    let output-string:String = {String {splice some-strings}}

    || Display the String.
    output-string
}