{define-proc {always-the-same-example foo, bar, baz} || body of always-the-same-example }When calling, each argument must be supplied in order:
{always-the-same-example 1, "two", 3feet}
{define-proc {named-parameters-example name = "", || id defaults to whatever name is, || regardless of whether name was supplied || or defaulted id = name, color = "black", size = 4pt} || body of named-parameters-example }When calling, these can be supplied in any order, or not supplied at all:
{named-parameters-example} {named-parameters-example size = 2in, name = "Howard"} {new MyClass, parameter-foo = "red", parameter-bar = "black"}This is useful for supplying attribute values in a style like HTML or XML. It is also convenient for code maintenance, because new parameters can be added in a way that does not require all the old callers to be updated.
{define-proc {trampoline-example ...} {the-real-processing 42, true, ...} }You can also iterate over the
...
arguments:
{define-proc {return-the-first-int ...} {for arg in ... do {if (arg isa int) then {return arg} } } {return null} } {return-the-first-int "hello", {GoodBye}, 42, 17.9, 3} || => 42
All of these can be combined together. The keyword arguments can be defined in any order, and supplied in any order. Real code is not typically so complicated, but it's nice to know it's possible.
{define-proc {combined-example name = "", foo, id = name, bar, baz, color = "black", size = 4pt, ...} || body of combined-example }In the following example calls:
foo
bar
baz
...
parameter:
{combined-example 1, "two", 3feet} {combined-example 1, "two", 3feet, 42, "some more data"} {combined-example 1, my-key = 12, "two", 3feet, 42, "some more data"} {combined-example id = 99, color = "red", 1, "two", 3feet, 42, "some more data"} {combined-example || same results as the previous call 1, "two", color = "red", 3feet, 42, id = 99, "some more data"}
get-report-example
takes one int
argument and returns three values: a String
, a
DateTime
, and a ReportData
:
{define-proc {get-report-example id:int}:(name:String, last-updated:DateTime data:ReportData) || compute values for name, last-updated, data {return name, last-updated, data} } || later on, we bind all three results from a call {let (this-report-name:String, updated:DateTime, results:ReportData) = {get-report 42) }
factorial
example given in the main article. Procedures can be defined
on-the-fly, sharing state with the environment
in which they
are defined. In this section, we will show the use creation and use
of a function factory that returns two procedures each time it is
called.
Not only are procedures first-class (i.e., they can be passed around
as ordinary data and kept in variables), but procedure types
(signatures) are also first-class. First we define a
Type
for procedure that takes
no arguments but
returns an int
each time it
is called:
{let constant Counter:Type = {proc-type {}:int}}Next we define a
Type
for procedure that
take an int
as argument, but
does not return anything:
{let constant Resetter:Type = {proc-type {int}:void}}Our factory takes an initial value for the count and returns a
Counter
and a
Resetter
{define-proc {make-counter count:int}:(Counter, Resetter) {let the-resetter:Resetter = {proc {new-count:int}:void set count = new-count } } {let the-counter:Counter = {proc {}:int let old-value:int = count set count = old-value + 1 {return old-value} } } {return the-counter, the-resetter} }Now we can make two sets of counters and resetters.
{let (counter-1:Counter, resetter-1:Resetter) = {make-counter 0} } {let (counter-2:Counter, resetter-2:Resetter) = {make-counter 100} } || call the first counter {counter-1} || => 0 (returns the initial count) || call it again {counter-1} || => 1 (the next count) || call the other counter {counter-2} || => 100 (the other initial value) {counter-2} || => 101 (the next count) || call the first conter again {counter-1} || => 2 (its next count was not affected) {resetter-1 10} (reset the first counter) {counter-1} || => 10 (the new value) {counter-1} || => 11 (the next value) {counter-2} || => 102 (the other counter was never reset)Some of this kind of state sharing (called closures) can be done with classes (or "inner classes" in some languages), but it is sometimes simpler to do it more directly. A common use for closures in the applets in the Curl content language is in event handlers for instances of common classes. The
on
operator shown here is
really creating a procedure that closes over the arguments of the
two-state-button
factory:
{define-proc {two-state-button off-label:Visual, on-label:Visual }:CommandButton {return {CommandButton label = off-label, {on Action at button:CommandButton do {if button.label == off-label then set button.label = on-label else set button.label = off-label } } } } } {two-state-button "Off", "On"} {two-state-button "Pick me", "Picked"}
evaluate
directly