Lambda Functions#

Since the ISON processor is based on replacing objects with other objects, lambda-functions are right around the corner. There are basically three additional language features needed to implement lambda functions:

  • A \(\lambda\)-function scope is defined via $L{}.

  • The parameters of a \(\lambda\)-function are denoted by %[idx], where [idx] is the parameter’s order index from left to right. For example, %0 is the first parameter, %1 the second, etc. You can also denote a parameter that is consumed by the function but not used, as %~[idx]. For example, use %~0 to ignore the first parameter given. You may wonder what this feature could possibly be good for. Well, it is needed to implement \(\lambda\)-calculus.

  • You can also defined named \(\lambda\)-function arguments with the syntax %[name]%.

  • To execute a \(\lambda\)-function, i.e. replace the placeholders %i with specific parameters, you have two possibilities:

    1. explicit call via $!{}, or

    2. implicit call via ${x, [arg 0], [arg 1], [named arg]=[value], [...]}, where x is the reference to a \(\lambda\)-function.

The following examples will make the usage of \(\lambda\)-functions clearer.

Basic Usage#

The following example demonstrates the basic syntax. The element greet is defined as a \(\lambda\)-function in the global variable scope, so that we can access it directly via ${greet}. The function argument is %0. Whatever is passed to the function as first argument is placed at the position of %0.

We execute the \(\lambda\)-function implicitly, by passing the arguments in the reference function ${}.

import ison

dicData = {
    "__globals__": {
        "greet": "$L{Hello %0}",
    },
    "say": "${greet, Christian}",
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "say": "Hello Christian"
}

You can also pass nothing at all, which replaces the corresponding positional argument with an empty string. This can also be helpful, if you have a lambda function without any parameters, which you just want to execute, for example to execute some random number generator.

import ison

dicData = {
    "__globals__": {
        "greet": "$L{Value%0: $rand.int{0,10}}",
    },
    "say": "${greet,}",
    "again": "${greet, ` next`}"
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "say": "Value: 7",
    "again": "Value next: 9"
}

Alternatively, you can name the lambda function arguments with the syntax %[name]%, as shown in this example.

import ison

dicData = {
    "__globals__": {
        "greet": "$L{Hello %who%}",
    },
    # You can use `` to pass everything within the back quotes as argument
    "say": "${greet, who=` > Christian < `}",
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "say": "Hello  > Christian < "
}

Note that a \(\lambda\)-function text block is treated as a literal object until all arguments have been replaced by a function call. In the following example, the ${x} is only replaced, once the \(\lambda\)-function is fully evaluated. Here is an example,

import ison

dicData = {
    "__globals__": {
        "x": "let's go",
        "greet": "$L{`Hello %0, $x`}",
    },
    "say": "$greet",
    "again": "${greet, Christian}",
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "say": "$L{`Hello %0, $x`}",
    "again": "Hello Christian, let's go"
}

Processing Lambda Arguments#

If a lambda argument is a data structure like a dictionary, you may want to process this further. Consider the following example,

import ison

dicData = {
    "__func_globals__": {
        "JustDict": "$L{%0}",
        "AsString": "$L{%0:%1}",
        "FromDict": "$L{${%0:%1}}",
    },
    "__locals__": {
        "dicA": {
            "a": 1,
            "b": 2,
        }
    },

    "result 1": "${JustDict, $dicA}",
    "result 2": "${AsString, $dicA, b}",
    "result 3": "${FromDict, $dicA, b}",
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "result 1": {
        "a": 1,
        "b": 2
    },
    "result 2": "{\"a\": 1, \"b\": 2}:b",
    "result 3": 2
}
  • JustDict: since %0 is the only element of the lambda function, the string result of this function can be converted to a JSON object. Therefore, the result is the data structure passed as argument.

  • AsString: because the resultant string after replacing the lambda arguments cannot be converted to a JSON object, and there is no additional ${} structure that could be parsed, the result is the string created from %0 and %1.

  • FromDict: because the resultant string after replacing the lambda arguments contains a ${} element, this element is parsed again. During processing of ${} the : is interpreted as an element access control symbol and so the string representation of just the %0 lambda argument is converted to a JSON object, which is a dictionary. Then this dictionary is accessed with the content of %1, which results in 2.

Lambdas of Lambdas#

The real power of \(\lambda\)-functions comes from the fact, that also \(\lambda\)-functions can be passed in as parameters and that they can be evaluated partially, resulting in a new \(\lambda\)-function.

import ison

dicData = {
    "__func_globals__": {
        "polite": "$L{my dear %0}",
        "greeting": "$L{Hello %0}",
        "greet_polite": "${greeting, $polite}",
    },
    "x": "${greeting, Christian}",
    "say": "${greet_polite, Christian}",
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "x": "Hello Christian",
    "say": "Hello my dear Christian"
}

Dictionary Lambdas#

It is at times useful to have a whole dictionary that is regarded as a lambda function. That is, the result of the lambda function is a dictionary and the lambda paramters vary elements of that dictionary. Here is an example:

import ison

dicData = {
    "__func_globals__": {
        "L_Data": {
            # This line declares this dictionary to be a lambda function.
            # It does not need to be the first entry, but this makes it clearer.
            # The content of the '__lambda_' should be a dictionary, 
            # which is currently ignored.
            "__lambda__": {},
            # The remainder is the content of the lambda function
            "a": "$int{%0}",
            "b": "%1"
        }
    },
    "result": "${L_Data, 1, 2}"
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "result": {
        "a": 1,
        "b": "2"
    }
}

The lambda dictionary can also declare local and global variables or other lambda functions.

import ison

dicData = {
    "__func_globals__": {
        "L_Data": {
            # This line declares this dictionary to be a lambda function.
            "__lambda__": {},
            # Variables declared within this dictionary
            "__locals__": {
                "dicData": "%0"
            },
            "a": "${dicData:%1}",
        }
    },
    "__locals__": {
        "dicA": {
            "x": 1,
            "y": 2
        }
    },
    "result": "${L_Data, ${dicA}, x}"
}

dicResult = ison.run.Run(xData=dicData, bPrintWarnings=True)
print(ison.run.ToString(dicResult))
{
    "result": {
        "a": 1
    }
}

List Lambdas#

Lists can also be declared as lambda functions, by making their first entry __lambda__, as in this example:

import ison

dicData = {
    "__func_globals__": {
        "L_Data": [ 
            # This line declares this dictionary to be a lambda function.
            # For a list, this needs to be the first entry.
            "__lambda__",
            # The remainder is the content of the lambda function
            "%1", "%0"
        ]
    },
    "result": "${L_Data, 1, 2}"
}

dicResult = ison.run.Run(xData=dicData)
print(ison.run.ToString(dicResult))
{
    "result": [
        "2",
        "1"
    ]
}