{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:foobarbaz...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