LoanScript

LoanScript is a scripting language used to define custom operations on LoanPASS data. Various screens in LoanPASS will allow you to input scripts. For example, the screen below allows you to populate Encompass lock request fields with LoanPASS data. The panel on the left selects which lock request field to populate, and the text editor on the right allows you to write LoanScript code that defines which LoanPASS data should be read and how it should be transformed into values appropriate for Encompass.

Don't worry about what the code means for now, we'll explain how to write LoanScript later on.

Getting more information

When writing LoanScript, sometimes you'll want more context or information about your code than the raw text can provide. There are a few ways to get these details. Again, don't worry about what this information means for now. It's only important that you know how to get it for later.

Type information

To get the type of data outputted by a section of your code, simply hover over that section with your mouse.

You can also see all of the variables, functions, and types available to use in your script in the pane on the right side of the screen.

Error descriptions

The editor inspects your code for common problems that may cause issues when the code is actually run. These errors will be displayed by a red squiggly line under the problematic sections. Again, you can hover your mouse over these sections to get detailed information about the problem.

Autocompletion

Some LoanScript code may require one value from a list to be written in a particular place. When this is the case, the editor will show a list of available options as you type. This list is narrowed to fit what you've already typed. You can either write out the entire option yourself, or you can select one from the list by either clicking on it or scrolling to it with the arrow keys and then pressing Enter or Tab.

The LoanScript language

This guide will explain LoanScript itself—what the code means, what you can do with it, and how to write it.

Basic types

Every script outputs exactly one piece of data, and this data can be one of several types. This list describes each type, and how to write a simple script that outputs a value of that type.

  • Blank: This is the simplest LoanScript type, and it represents a value of "nothing". This is equivalent to a blank/empty credit application field on the pricing screen, for example, or a null value in many other programming languages. Below is a script that outputs a blank value. Notice that all we've done is write the value—that's the script's output. Every LoanScript script must end with a value or something that produces a value (called an "expression", more on that later). That final value is what the script outputs.
    blank
    
  • Boolean: This is a simple true or false value.
    true
    
    false
    
  • Number: This can be any decimal or integer number and may be positive, negative, or zero. These can be written like 3, 1.2, 0, -100, or -12.5, for example.
    -12.5
    
  • Text: This can be any piece of text and must be surrounded by double quotes.
    "Hello, world!"
    
  • Date: Dates can be created by writing the date in Month/Day/Year format enclosed in DATE(...), like the code below, which has the value June 12th, 2023:
    DATE(6/12/2023) 
    
  • Day duration: This is any length of time that can be specified in an exact number of days. This is written as a number followed by the keyword day, days, week, or weeks. The duration 1 week is exactly equal to 7 days, 2 weeks is 14 days, etc. Pluralization is not enforced (for example, you can write 2 week if you wish), but is encouraged to make your scripts more readable.
    45 days
    
  • Month duration: This is any length of time that can be specified in an exact number of months. This is written as a number followed by month, months, quarter, quarters, year, or years. The duration 1 quarter is exactly equal to 3 months and 1 year is equal to 4 quarters or 12 months. The same pluralization rules (or lack thereof) apply to this type as well.
    30 years
    

Variables

A variable is simply a name that represents a value. This code creates a variable named x that represents the value 5

let x = 5

Variable assignment statements like the one above do not output anything, so that line by itself is not a valid script, because a script must have an output. The script below is valid. It outputs the number 5 by assigning it to x and then outputting x:

let x = 5

x

Comments

Sometimes it's useful to write notes in your code. You can do this by writing two forward slashes followed by your comment. Everything on the same line after the slashes will not be executed.

// This is a comment to explain the script below

let my_variable = 123 // You can put comments on the same line as your code

// You can also put comments between lines of code.
// If you want multiple lines of comments 

my_variable

// This is a comment after the script

Complex types

LoanScript supports a few types that are actually collections of pieces of data of simpler types.

  • List: This is a series of values. Values in a list may be of the same type or of different types. They are separated by commas and surrounded by square brackets.

    [1, 2, 3]
    

    or

    [12.5, "some text", true, 25 years]
    

    The items in a list can be accessed by appending square brackets to a list value and putting the position of the desired item between the brackets. Positions start at zero, so the position of the first item is 0, the position of the second is 1, and so on. For example my_list[1] outputs the second item in my_list. This script creates a list of 3 names and then outputs the second one ("Alice"):

    let people = [
       "Fred",
       "Alice",
       "Leslie"
    ]
    
    people[1]
    
  • Object: An object is similar to a list in that it may contain multiple other values of different types. However, each value that it contains is given a name. Objects are defined inside curly brackets and contain pairs of names and values (called "properties") separated by colons. Each pair is separated with a comma, like this:

    {
        some_text: "Hello world!",
        a_number: -12.5,
        my_list: [true, 12 years]
    }
    

    Objects may even contain other objects:

    {
        some_text: "Hello world!",
        a_number: -12.5,
        an_inner_object: {
            a_boolean: false,
            some_blank_property: blank
        },
        my_list: [true, 12 years]
    }
    

    Notice how the property names contain multiple words but use underscores instead of spaces. This is not the only way to name things, but there are some rules around names in LoanScript:

    • No spaces are allowed
    • Only letters, digits, and underscores are allowed
    • Names must not begin with a digit

    Among programming languages, there are many different "casing conventions", or ways of naming things consistently. The convention with lowercase words and underscores above is called "snake casing" in programmer lingo. Most LoanScript scripts will have access to pre-defined data that will generally follow this convention, so for consistency, we recommend following it when you name things yourself.

    The properties of an object are accessed by putting a dot (.) after an object value and then putting the name of the property after the dot. This script creates an object named person with a few pieces of personal information and then outputs the person's name.

    let person = {
       name: "Fred",
       email_address: "fred@example.com",
       age: 35
    }
    
    person.name // "Fred"
    

    Nothing is done with the user's email_address or age properties.

  • Enumeration: Enumerations are groups of options (called "variants"), similar to what you would see in a dropdown list. There is no way to create custom enumerations directly in LoanScript, but the ones created in LoanPASS's enumeration editor are available for use in your scripts. For example, suppose your LoanPASS account has an enumeration called "Occupancy Type" with the options "Primary residence" and "Investment". You want to write a script that outputs the "Primary residence" option. You can access this variant using the name of the enumeration followed by a dot and then the name of the variant, similar to the way you access properties of objects:

    OccupancyType.primary_residence
    

    Notice that the name OccupancyType does not follow the "snake casing" convention described earlier. This type of casing (all the words run together and capitalized) is known as "Pascal casing", and it's generally used to name types, where snake casing is used to name values. An enumeration is a type, not a value. A type describes what kind of data a value can be. The value is the actual concrete data. The OccupancyType enumeration is a type because it defines that "an OccupancyType may be either an investment or a primary residence". The value is which of these two options is actually selected. The script above outputs the value, which is primary_residence.

Operations

LoanScript support some basic operations (addition, subtraction, multiplication, division, and exponentiation) that can be performed on numbers as well as a few other types where applicable. It also supports the use of parentheses and follows the PEMDAS (Parentheses, Exponents, Multiplication, Division, Addition, Subtraction) order of operations.

  • Addition: This operation uses the + symbol and adds two values together. It can be used on a number of different types.

    • Numbers: 2 + 3 outputs 5.
    • Day durations: 1 week + 2 days outputs 9 days.
    • Month durations: 1 year + 2 months outputs 14 months.
    • Dates and day durations: DATE(6/12/2023) + 1 week outputs DATE(6/19/2023).
    • Dates and month durations: DATE(6/12/2023) + 2 months outputs DATE(8/12/2023).
    • Text: "Hello" + " World!" outputs "Hello World!". Notice that this isn't mathematical addition like the above usages. When you use + on two pieces of text, it joins ("concatentates") them together.
    • Lists: [1, 2, 3] + [true, false] outputs [1, 2, 3, true, false]. Like with text, using + on lists concatenates them into a single list.
  • Subtraction: This operation uses the - symbol to subtract the second value from the first. It accepts slightly fewer types than addition:

    • Numbers: 7 - 4 outputs 3.
    • Day durations: 1 week - 2 days outputs 5 days.
    • Month durations: 1 year - 2 months outputs 10 months.
    • Dates and day durations: DATE(6/12/2023) - 1 week outputs DATE(6/5/2023).
    • Dates and month durations: DATE(6/12/2023) - 2 months outputs DATE(4/12/2023).
  • Multiplication: This operation uses the * symbol to multiply two values:

    • Numbers: 3 * 4 outputs 12.
    • Day durations and numbers: 1 week * 2 outputs 2 weeks.
    • Month durations and numbers: 2 months * 3 outputs 6 months.
  • Division: This operation uses the / symbol and divides the first value by the second:

    • Numbers: 12 / 4 outputs 3.
    • Day durations and numbers: 4 weeks / 2 outputs 2 weeks.
    • Month durations and numbers: 6 months / 2 outputs 3 months.
  • Exponentiation: This operation uses the ^ symbol to raise the first value to the power of the second. It is only usable with numbers.

    • Numbers: 3 ^ 2 outputs 9 (three squared).
  • Negation: This operation uses the - symbol to flip a single value from positive to negative or vice versa. For example, this script outputs -5:

    let x = 5
    let negative_x = -x;
    negative_x // -5
    

    You can of course use it directly to write negative numbers, like this:

    let x = -5
    x // -5
    

    It also works on day and month durations. This script outputs DATE(6/5/2023) because it adds "negative one week" to DATE(6/12/2023), which is the same as subtracting a week:

    let duration = 1 week
    let negative_duration = -duration
    DATE(6/12/2023) + negative_duration
    
  • Order of Operations and Parentheses: This script outputs 14 because the multiplication is performed first, even though the addition is "first" from left to right:

    2 + 3 * 4
    

    But this one outputs 20 because the parentheses cause the addition to be performed first:

    (2 + 3) * 4
    

Functions

Functions are expressions that you can define once and use multiple times. You can create and use one like this:

let add = (a, b) -> { a + b }

// outputs 5
add(2, 3) 

This creates a simple addition function that can take two values, add them together, and output the result. The script above assigns this function to the variable add. The function's inputs, called "arguments", are in the comma-serparated list after the = symbol. You can think of these as variables that only exist inside the function's "body", which is the part between the { and } brackets. These two variables are added together with + and the function outputs the result of that operation.

The variables are given their values when the function is used, or "called", which is what happens on the line add(2, 3). A "call" is written by writing the name of the function (add) and following it with a pair of parentheses that contain a comma-separated list of the values to be assigned to the arguments. So in this example, a is assigned the value of 2 and b is assigned the value of 3, so the function outputs 2 + 3, or 5.

The example function defined above can be used when a and b are given any two types for which addition is valid. For example, all of these calls are valid:

let sum = add(2, 3) // 5
let concatenated_text = add("Hello ", "World") // "Hello World"
let concatenated_list = add([1, 2], [3, 4]) // [1, 2, 3, 4]
let date_plus_duration = add(DATE(6/12/2023), 2 weeks) // DATE(6/26/2023)

If you want to restrict the types of the arguments, you can annotate them with type names. For example, if you want the add function to only add two numbers:

let add = (number a, number b) -> { a + b }

let sum = add(2, 3) // This is valid and outputs 5
let invalid = add("Hello ", "World") // This is invalid and will show an error in the editor

The following type names can be used in this way:

  • blank
  • number
  • boolean
  • text
  • date
  • any
  • day duration
  • month duration

LoanScript comes with some built-in functions for your convenience. You can view a list of these under the "Functions" section of the pane to the right of the code editor.

Logicl operations

LoanScript supports some additional operations that are only valid when used with boolean values: and, or, and not.

  • And: The and keyword takes two boolean values or expressions and outputs true only if both are true. If one or both are false, the output will be false.

    This script outputs true:

    let has_good_credit = true
    let has_enough_income = true
    
    let is_approved = has_good_credit and has_enough_income
    
    is_approved // true
    

    This outputs false:

    let has_good_credit = true
    let has_enough_income = false
    
    let is_approved = has_good_credit and has_enough_income
    
    is_approved // false
    
  • Or: The or keyword takes two boolean values or expression and outputs true if either or both are true. It will only output false if both are false.

    This script outputs true:

    let is_investment_property = false
    let is_primary_residence = true
    
    let loan_purpose_is_valid = is_investment_property or is_primary_residence
    
    loan_purpose_is_valid // true
    

    This outputs false:

    let is_investment_property = false
    let is_primary_residence = false
    
    let loan_purpose_is_valid = is_investment_property or is_primary_residence
    
    loan_purpose_is_valid // false
    
  • Not: The not keyword flips the value of a single boolean, turning true into false and false into true.

    This script outputs true:

    let has_bankruptcy = false
    
    let is_approved = not has_bankruptcy
    
    is_approved // true
    

    This script outputs false:

    let has_bankruptcy = true
    
    let is_approved = not has_bankruptcy
    
    is_approved // false
    

Comparison

LoanScript supports several comparison operations, all of which output a boolean value of true or false. These operations are supported:

  • Equals: Equality comparison uses the "double equals" symbol (==) to distinguish it from variable assignment, which uses a single equals sign (=). It outputs true if the two values are exactly the same. This script outputs true:

    3 == 3 // true
    

    This outputs false:

    3 == 4 // false
    

    This comparison can be done on values of any two types (they do not have to be the same). It will only output true if both values are of the same type and the same value. For example, this will output false because one value is a number and the other is text, but it's a perfectly valid expression:

    5 == "5" // false
    
  • Does not equal: Inequality comparison uses the <> symbol. It outputs false only if the two values are exactly the same. This script outputs true:

    3 <> 4
    

    This outputs false:

    3 <> 3 // false
    

    Like the == comparison, this can be used with any combination of types. It will always output true if the types are different. For example, this script outputs true because the values are of different types, and are therefore not equal to each other:

    5 <> "5" // true
    
  • Greater than: Greater-than comparison uses the > symbol and output true if the first (left) value is larger than the second (right) value. If the values are equal or the first value is smaller, it outputs false. It is only usable with certain types.

    • Numbers: 4 > 3 outputs true, while 3 > 4 outputs false.

    • Day durations: 1 week > 3 days outputs true, while 3 days > 1 week outputs false.

    • Month durations: 1 year > 3 months outputs true, while 3 months > 1 year outputs false.

    • Dates: A date is "greater" than another date if it comes later chronologically. So DATE(6/30/2023) > DATE(6/1/2023) outputs true, while DATE(6/1/2023) > DATE(6/30/2023) outputs false.

    • Text: A piece of text is "greater" than another text if it comes later in alphabetical order. So "foo" > "bar" outputs true, while "bar" > "foo" outputs false.

      Note that uppercase letters are considered "first" in alphabetical ordering, so even though "b" > "a" is true, "B" > "a" is false. If you want to ignore casing when comparing text, you should convert both text values to the same case before comparing using the uppercase() and lowercase() functions. For example, this script outputs true:

      let letter_b = "B"
      let letter_a = "a"
      
      // Compares "b" with "a", not "B" with "a"
      lowercase(letter_b) > lowercase(letter_a) // true
      
  • Greater than or equal: Greater-than-or-equal comparison uses the >= symbol. This comparison is valid for the same types as >, except that it also outputs true if the values are equal to each other.

  • Less than: Less-than comparison uses the < symbol and output true if the first (left) value is smaller than the second (right) value. If the values are equal or the first value is larger, it outputs false. It is only usable with certain types.

    • Numbers: 3 < 4 outputs true, while 4 < 3 outputs false.

    • Day durations: 3 days < 1 week outputs true, while 1 week < 3 days outputs false.

    • Month durations: 3 months < 1 year outputs true, while 1 year < 3 months outputs false.

    • Dates: A date is "smaller" than another date if it comes first chronologically. So DATE(6/1/2023) < DATE(6/30/2023) outputs true, while DATE(6/30/2023) < DATE(6/1/2023) outputs false.

    • Text: A piece of text is "smaller" than another text if it comes first in alphabetical order. So "bar" < "foo" outputs true, while "foo" < "bar" outputs false.

      Note that uppercase letters are considered "first" in alphabetical ordering, so even though "a" < "b" is true, "a" < "B" is false. If you want to ignore casing when comparing text, you should convert both text values to the same case before comparing using the uppercase() and lowercase() functions. For example, this script outputs true:

      let letter_a = "a"
      let letter_b = "B"
      
      // Compares "a" with "b", not "a" with "B"
      lowercase(letter_a) < lowercase(letter_b) // true
      
  • Less than or equal: Less-than-or-equal comparison uses the <= symbol. This comparison is valid for the same types as <, except that it also outputs true if the values are equal to each other.

Conditions

A common use case in LoanScript is to output different values based on whether some condition is met. This is done with an if {condition} then {value when true} else {value when false} expression. This expression evaluates the code in {condition} (which is just a placeholder for the actual code, which you'll see shortly), which must always output a boolean (true/false) value. If the condition is true, then the script outputs the value of {value when true}, otherwise it outputs {value when false}. For example, this script will always return the text "It's true!":

if true then "It's true!" else "It's not true."

While this one will always output `"It's not true.":

if false then "It's true!" else "It's not true."

Obviously this isn't very useful, since the condition never changes. For a slightly more realistic example, let's say you're writing a script that grades an applicant's credit score and outputs a message telling them about it.

Note: If you've been following along by trying out code in the editor, don't enter the code in the rest of this section just yet. It won't actually work, for a reason we'll get to in the next section.

if Loanpass.credit_application_fields.decision_credit_score > 680 then "You have great credit!" else "You have bad credit."

This will output "You have great credit!" as long as the score is higher than 680, and "You have bad credit" otherwise.

But what if we want to categorize the credit score into more than two "buckets"? That's easy with a little additional code, but before we move on, lets clean up this code a bit. That's a pretty long line and it might require horizontal scrolling in the editor, depending on your screen size.

For more credit score buckets, we're going to need more comparisons on Loanpass.credit_application_fields.decision_credit_score property, but that a lot to type out for every one of them. So let's assign that to a variable called score and just compare against that.

let score = Loanpass.credit_application_fields.decision_credit_score

if score >= 740 then "You have great credit!" else "You have bad credit."

That's already a bit easier to read. But let's break up this line so it's easier to visually parse where the condition and the values are.

let score = Loanpass.credit_application_fields.decision_credit_score

if score >= 740 then 
   "You have great credit!" 
else 
   "You have bad credit."

Now it's easy to see all the parts of the if-expression at a glance. It's generally great practice to put the values on their own lines and indent them like this.

Now for the additional buckets. Let's say we want to add a "good" bucket between "bad" and "great". Values in the if-expression don't have to be simple—we can put more complex expressions there to output even more values. So let's add another if-expression inside the else clause.

let score = Loanpass.credit_application_fields.decision_credit_score

if score >= 740 then 
   "You have great credit!" 
else 
   if score >= 670 then
      "You have good credit."
   else 
      "You have bad credit."

In this script, if the score is below 720, then we enter the first else clause, where we evaluate whether the score is 670 or above. If it is, then we say the applicant has "good" credit.

Let's add another condition, giving us four categories: "great", "good", "fair", and "bad".

let score = Loanpass.credit_application_fields.decision_credit_score

if score >= 740 then 
   "You have great credit!" 
else 
   if score >= 670 then
      "You have good credit."
   else 
      if score >= 580 then
         "You have fair credit."
      else
         "You have bad credit."

The indentation rule that was supposed to make our code more readable is starting to feel counterproductive. Let's collapse the spacing between all those elses and ifs.

let score = Loanpass.credit_application_fields.decision_credit_score

if score >= 740 then 
   "You have great credit!" 
else if score >= 670 then
   "You have good credit."
else if score >= 580 then
   "You have fair credit."
else
   "You have bad credit."

Now we can easily read each credit tier from the top down.

Notice how our experiments with spacing didn't affect the meaning of the code—it's valid either way. This is because "whitespace" (spaces, tabs, and newlines) do not have any meaning in LoanScript other than being necessary to separate keywords and names. We could run this entire script together on one line or put every word on a different line and it would still be valid. But following spacing conventions like the ones demonstrated above is a good practice to make sure your code remains easy to read and write as it becomes more complex.

Multiple types and type guards

The above script will not actually work in a real LoanPASS tenant account without a minor tweak. This is because fields on the credit application form can be left blank, so Loanpass.credit_application_fields.decision_credit_score, and thus the score variable, is not necessarily a number. It may be a number, but it may also be blank. This is a problem because if it's blank, then the conditions in the if-expression are nonsensical. Is blank >= 740 true or false? Keep in mind that blank is not zero, it's just blank, i.e., it has no value at all.

We need some way to only evaluate the if-expression if score is a number, and output some other message ("We don't know what your credit score is!") if it's blank. We can use another if-statement and a special expression known as a "type guard" to do this. These are written as <some variable> is <some type> or <some variable> is not <some type>. This outputs a boolean value, so we can use it as the condition in an if-expression. We can construct a new if-expression and "wrap it around" the one from the previous section so that it only gets evaluated if score is a number:

let score = Loanpass.credit_application_fields.decision_credit_score

if score is number then 
   if score >= 740 then 
      "You have great credit!" 
   else if score >= 670 then
      "You have good credit."
   else if score >= 580 then
      "You have fair credit."
   else
      "You have bad credit."
else 
   "We don't know what your credit score is!"

The above script only executes the original if-expression if the condition score is number is true. If it's not a number then it must be blank, so we output a message saying so ("We don't know what your credit score is!").

The is keyword is a special type of comparison that outputs a true/false value like the others, but the right-hand side of it must be a type, not a value. We could also write this condition as score is not blank. Since the only other possibility is number, that would also make the comparisons valid.

The following type names can be used in this way:

  • blank
  • number
  • boolean
  • text
  • date
  • any
  • day duration
  • month duration

List operations

LoanScript supports several operations that are unique to lists and allow you to do things like add a list of numbers, sort a list of text alphabetically, and filter out unwanted values.

  • Replace: The replace operation runs a function on every item in a list and outputs a new list that contains the outputs of the function. Its syntax looks like this:

    <list> replace with <function>
    

    For example, this expression takes a list of numbers and outputs a list with all the numbers doubled:

    // outputs [2, 4, 6]
    [1, 2, 3] replace with (item) -> { item * 2 }
    

    The function can optionally take a second argument, which is the index of the item in the list. Remember that indices start at zero. This expression takes every number in a list and multiples it by the index of that number in the list:

    // outputs [0, 2, 6] (by multiplying [1 * 0, 2 * 1, 3 * 2])
    [1, 2, 3] replace with (item, index) -> { item * index }
    
  • Filter: The filter operation runs a function on every item in a list. That function must return a boolean true/false values that says whether or not the item should be included in the output. Its syntax looks like this:

    <list> filter with <function>
    

    For example, this expression takes a list and removes all the values less than 3:

    // Outputs [3, 4, 5]
    [1, 2, 3, 4, 5] filter with (item) -> { item >= 3 }
    

    filter also takes an optional index argument. This expression outputs all but the first two names in a list:

    // outputs ["Steve", "Joan"]
    ["Bob", "Alice", "Steve", "Joan"] filter with (item, index) -> { index >= 2 }
    
  • Sort: The sort operation runs a sorting function on pairs of list items and outputs a list with the same values as the original list, but in sorted order. Its syntax looks like this:

    <list> sort with <function>
    

    The function should output a boolean that says whether the first and second argument are in the correct order. For example, the code below sorts a list of numbers from smallest to largest. If a < b, then the items are in the correct order (smaller then larger) and the function returns true.

    // Outputs [-3, 2, 4, 6]
    [4, 2, 6, -3] sort with (a, b) -> { a < b }
    

    The function is called repeatedly during sorting, with the arguments a and b being assigned pairs of list items in the order that they currently appear in the list. For example, the function will be called first with a == 4 and b == 2, so the comparison will be 4 < 2, which is false. This tells the sorting operation that these items are in the wrong order and need to be swapped, so the list becomes [2, 4, 6, -3]. Then the next pair, which is now 4 and 6, is checked. The function body will return true this time, so these will not be swapped. This process continues until no more swaps are needed. At this point the list is sorted.

  • Combine: The combine operation is used to aggregate all the items in a list into a single value. Its syntax looks like this:

    <list> combine from <initial_value> with <function>
    

    Notice the from <initial_value>, which is a bit different from the previously described operations. This is a starting value on which to build the final output. For a simple example, this expression sums a list of numbers:

    // Outputs 12 (0 + 2 + 4 + 6)
    [2, 4, 6] combine from 0 with (total, item) -> { total + item }
    

    The first argument, which we've chosen to call total here, receives the output of the function from the last time it was evaluated. Since there is no "last time" the first time the function is called, it receives the initial value of 0 for that call (specified by us with from 0). So for the first call, the arguments are 0 and 2, which are added to produce 2. On the next iteration, total will be 2 and item will be 4, which add up to 6. Finally, the arguments are 6 and 6, which add up to 12. Since there are no more list items, 12 becomes the output. You could of course start with a different value, for example 10:

    // Outputs 22 (10 + 2 + 4 + 6)
    [2, 4, 6] combine from 10 with (total, item) -> { total + item }
    

    Like some of the other list operations, this operation takes an optional argument for the index of the item in the list. This expression sums all the items in a list except for the first two:

    // Outputs 14 (6 + 8)
    [2, 4, 6, 8] combine from 0 with (total, item, index) -> { 
        if index >= 2 then 
           total + item
        else 
           0
    }   
    

    Until the third item, the expression adds 0 to the total, effectively skipping the first two.

Any list operation that outputs a list (which most of them do) can be directly followed by another list operation, allowing us to "chain" list operations. This can often be used to break up operations so that the intent of an expression is more obvious from reading its code. For example, let's take a second look at the sample code from above where we add up all but the first two numbers in a list:

[2, 4, 6, 8] combine from 0 with (total, item, index) -> { 
    if index >= 2 then 
       total + item
    else 
       0
}   

It would be easier to understand the intent of this expression if we explicitly filtered out the unwanted items before summing them. We can in fact do this by chaining multiple list operations:

// outputs 14
[2, 4, 6, 8] 
   filter with (item, index) -> { index >= 2 } // Removes the first two items (2 and 4)
   combine from 0 with (total, item) -> { total + item } // Adds the remaining items (6 and 8)

This expression drops the first two values using filter with, then sums the rest, which gives the same result (14). This way of writing it neatly separates the operations into two distinct phases.