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:
{
"type": "APLA",
"version": "0.91",
"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.91) |
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.91
, 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.
{
"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
.
{
"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.
Last updated: Nov 28, 2023