Curl™ Content Language Features

Some of the features of the Curl language are described here: There is a lot of programmer experience with these capabilities, and they are largely accepted and expected in contemporary languages. The real innovation of Curl™ technology is in how these features are supported and used by the implementation and libraries.

Types


Flexible Calling

Required positional arguments:

Parameters to procedures and methods can be declared similarly to most languages, like this:
  {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}
  

Optional named (keyword) arguments with default values:

Parameters can be given default values:
  {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.

Variable number of unspecified (rest) arguments:

A procedure can take any number of arguments, and these can be spliced into another call.
  {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:
1 is always the first argument and is bound to foo
"two" is always the second argument and is bound to bar
3feet is always the third argument and is bound to baz
When they appear, 42, "some-more-data", and my-key = 12 are included in the ...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"}
  

Multiple return values:

Instead of having to define "out" parameters that will be modified to give additional values to the caller, a procedure or method can just return however many values it needs to. The procedure 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)
  }
  

Functional (prodedure-based) Programming

Powerful functions

The Curl language allows recursive function definition, such as the classic 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"}

top-level procedures (not associated with any class)

  • full closures (anonymous procedures with sharable state)
  • recursion

    direct programmatic evaluation of code


    Object-Oriented Programming


    Other Language Features

    Modularity and Security:

    automatic memory management (garbage collection)

    try/catch style exception handling, with exceptions modeled as classes

    programmer-extensible iterators