Data-binding Evaluation (APL for Audio)

APL for Audio relies on data-binding to incorporate user-provided data, include audio resources, and conditionally inflate components based on data. To use data-binding in your document, you write data-binding expressions, which are JSON strings with an embedded ${...} substring. This document describes how Alexa evaluates these expressions.

For more details about the syntax for data-binding expressions, see Data-binding Syntax.

For details about how you define a data source, see Data Sources

Data-binding algorithm

Consider the following APL document:

Copied to clipboard.

{
  "type": "APLA",
  "version": "0.9",
  "mainTemplate": {
    "parameters": [
      "data"
    ],
    "item": {
      "type": "Speech",
      "content": "There are ${payload.data.value} peas in the pod"
    }
  }
}

In this example the Speech component uses the value from the data.value property from a data source. This value is inserted into a longer expression.

The following sections describe the algorithm used to build this string with the results of the ${payload.data.value} expression.

Step 1: Expression evaluation

If the right-hand side (RHS) is a string, scan the string for data-binding expressions (embedded '${…}'). If the scanner finds one or more embedded expressions, the string is converted to an abstract syntax tree (AST). For example, the text example above translates to the tree:

Concatenate
  String("There are ")
  AttributeLookup
    Symbol("data")
    String("value")
  String(" peas in the pod")

The AST evaluates using the current data-binding context. The data-binding context is a JSON dictionary of key-value pairs, where each value is a number, boolean, object, array, or string. Operators such as “+” do implicit type conversion as necessary. For example, assume the data-binding context contains the following:

{
  "data": {
    "value": 5
  }
}

The data symbol returns the object {value: 5} and the attribute accessor of value returns the number 5. The ternary concatenation operator has two strings and a number, so it casts the number as a string and returns “There are 5 peas in the pod” as a string value.

Step 3: Type coercion

The final step is to ensure that the set value is of the correct type. Example type conversions are shown in the table in Truthy and coercion.

Initial data-binding context

When Alexa inflates an APL document, Alexa assigns the document a new data-binding context that contains the following pre-defined objects:

Name Description
environment Information about the current runtime environment
Math Built-in mathematical functions (see Data-binding Syntax)
String Built-in string functions (see Data-binding Syntax)

environment

The environment object contains runtime information about the operating APL environment. It contains the following properties:

Name Type Description
alexaLocale String The current locale of the request.
agentName String Name of the runtime environment
agentVersion String Version of the runtime environment
aplaVersion String Supported version of APL for Audio (currently 0.9)
aplVersion String Supported version of APL (null if none exists)
apltVersion String Supported version of APL for character displays (null if none exists)

For example, if environment.aplaVersion is 0.9, and both aplVersion and apltVersion are null, the device supports APL for Audio, but doesn't have a screen.

Extending the data-binding context

Composition extension

APL for audio compositions have parameters. Inflating a composition adds the named parameters to the data-binding context.

The following example shows a simple composition that uses SSMl to apply the "Joey" voice.

Copied to clipboard.

{
  "compositions": {
    "JoeyVoice": {
      "description": "A basic wrapper around a speech component to always apply the Joey voice",
      "parameters": [
        {
          "name": "speechContent",
          "type": "string"
        }
      ],
      "item": {
        "type": "Speech",
        "contentType": "SSML",
        "content": "<speak><voice name='Joey'><lang xml:lang='en-US'>${speechContent}</lang></voice></speak>"
      }
    }
  }
}

You reference the custom composition with the document mainTemplate.

Copied to clipboard.

{
  "type": "JoeyVoice",
  "when": "${environment.alexaLocale == 'en-US'}",
  "speechContent": "Hello!"
}

In this example, speechContent is a parameter for the custom composition JoeyVoice. When you use JoeyVoice in your document, Alexa adds the speechContent parameter and its value to the data-binding context. This is what lets the composition refer to the value of the parameter with an expression such as ${speechContent}.

If you don't provide a value for speechContent, Alexa adds the parameter with its default value to the data-binding context. You can specify parameter defaults when you define the composition. The above example didn't define a default for speechContent, so the default value in this example is null.

This augmented data-binding context is valid when inflating the custom composition and its child components.

Component child extension

Some components have multiple children, such as Sequencer. Components with multiple children add the following global names to the data-binding context:

Name Description
data Data assigned from the data property during component inflation. For more details about data, see Data array inflation
index The 0-based index of the current child
length The total number of children in the current component

Note that data is set when you set the data property for the multiple-child component.

Data-binding with arrays

Many APL expressions involve evaluating an array. APL supports type coercion of arrays, implicit array-ification, and interpolation of data-bound expressions into arrays.

Array type coercion

When a property definition specifies an array of a known type, each element in the array is coerced to that type during property assignment. For example, a composition expecting an array of numbers coerces each element of that array into a number during assignment.

Implicit array-ification

For convenience, all APL properties that take an array of values also accept a single property without the array brackets. For example, the items property of a Sequencer takes an array of components. When passing a single item to items, these approaches are equivalent:

"item": {<<ITEM>>}
"item": [ {<<ITEM>>} ]

The APL runtime expands both of these into an array of length 1.

Properties that expect an array have a plural alias. For example, item and items are the same property. This keeps the code more legible.

Interpolation of data-bound expressions into arrays

Array expansion supports the interpolation of arrays into arrays. For example:

// Context
{
  "a": "apple",
  "b": [ "alpha", "bravo" ]
}

"values": "${a}" -> values = [ "apple" ] // Implicit array-ification
"values": [ "${a}" ] -> values = [ "apple" ]
"values": "${b}" -> values = [ "alpha", "bravo" ]
"values": [ "x", "${b}", "${a}" ] -> values = [ "x", "alpha", "bravo", "apple" ]

The rules of array-ification are:

  • When the value is a string, evaluate it using data-binding and coerce to the correct type (and apply array-ification).
  • When the value is an array:
    • For each element of the array that is a string, evaluate it using data-binding. If the result is a single item, insert it in the array.
    • For each element of the array that is an array of items, insert all the items into the array.