Types in the Alexa Conversations Description Language
The Alexa Conversations Description Language (ACDL) uses a nominal type system. In a nominal type system, compatibility of types is determined by explicit declarations and/or the name of the types.
In ACDL, a type has the following elements:
- A fully qualified name.
- An optional set of properties. Each property has a type. You're not required to set the optional properties to an expression.
- Generic parameters that the ACDL compiler infers during compilation.
For types specific to the Alexa Conversations Core Library (ACCL), see Types in the ACCL.
- Primitive types
- Declare types
- Type inheritance
- Type parameters
- Generic types
- List
- Type compatibility rules
- Recursive types
- Slot types
- Related topics
Primitive types
ACDL has the following primitive types. Primitive types are declared in the com.amazon.alexa.schema
namespace.
You can declare a name of a primitive type by using a literal of the corresponding type.
String
You can declare names of the String
type by using string literals. In the following example, hello
is a string literal and nameA
is a name of type String
.
nameA = "hello"
Number
You can declare names of the Number
type by using literals. In the following example, 123.321
is a number literal and nameA
is a name of type Number
.
nameA = 123.321
Boolean
The Boolean type has two possible values, Boolean literals true
and false
. In the following example, true
is a Boolean literal and nameA
is a name of type Boolean
.
nameA = true
Nothing
The Nothing
type is a special type in ACDL; it's a subtype of all types and has only one possible value, the nothing
literal. You can assign the nothing literal to any type in ACDL.
Thing
Thing
is the parent type of all types in ACDL. All types are compatible with Thing
. Consequently, you can declare any expression as an argument to an action or dialog if the action or dialog requires an argument of type Thing
.
The Thing
type is declared in the com.amazon.alexa.schema
namespace. In the following example, the response
action is declared with an argument of type Thing
, but the invocation assigns a WeatherPayload
type to that argument.
import prompts.weatherPrompt
import prompts.WeatherPayload
action Nothing response(Response response, Thing payload)
response(weatherPrompt, WeatherPayload {highTemp = 100, lowTemp = 50}) // Valid because Thing is a super-type of `WeatherPayload`
Declare types
In ACDL, you declare a type by using the type
keyword. In the following example, the User
type is declared with two properties, firstName
and lastName
. Both properties are required.
Boolean
.namespace org.example.mydialog
import com.amazon.alexa.schema.String
type User {
String firstName
String lastName
}
The following example shows how to declare new types by using other types.
namespace org.example.mydialog
type FirstName {
String name
}
type LastName {
String name
}
type User {
FirstName firstName
LastName lastName
}
You can mark a property as optional by using the optional
keyword as follows.
namespace org.example.mydialog
import com.amazon.alexa.schema.String
type User {
String firstName
optional String lastName
}
Type inheritance
ACDL supports type inheritance, which is typical for nominal type systems. In type inheritance, the type that inherits another type is the subtype of the inherited type, and the inherited type is the super-type. The subtype contains the union of all properties from both types. You can declare the same property (that is, the name and type) in more than one of the types in the inheritance.
The following example shows type inheritance.
type A {
NameA nameA
}
type B : A {
NameB nameB
}
// Valid
type C : A {
NameA nameA
}
// Invalid
type D : A {
NameB nameA
}
In the previous example, type B
now has two properties: nameA
and nameB
. The declaration of type C
is valid because the property it declares is exactly the same (that is, the same name and type) as the property in A
. However, the declaration of type D
is not valid because the union of the properties of A
and D
properties can't contain two properties with the same name but different types.
ACDL also supports multiple inheritance. When inheriting multiple types, the same union rule for properties applies. The following example shows how to inherit multiple types.
type A {
NameA nameA
}
type B : A {
NameB nameB
}
type C : A, B {
}
type D : A, B, C {
NameA nameA
}
In the previous example, type C
now contains the properties of A
and B
. The declaration of type D
is also valid, even though the property nameA
is part of A
, C
, and D
.
Recursion in type inheritance is not allowed. In other words, a type T
can't inherit itself.
// Invalid
type A : C {}
type C : A {}
In the previous example, types A
and C
indirectly inherit themselves.
Type inheritance restrictions
Alexa Conversations currently supports type inheritance for the types declared in the ACCL. You can't inherit custom types you declare.
Type parameters
A type parameter is a placeholder for a specific type in a type declaration or an action declaration.
The scope of the type parameter in a type declaration is limited to the type declaration block and the type parameters specified in super-types. The scope of the type parameter in an action declaration is limited to the arguments and return type of the action.
Every type parameter has a type bound. If you don't declare a bound for a type parameter, the ACDL compiler sets the type to Thing
.
In the following example, T
is the type parameter and Fruit
is the type bound.
T : Fruit
Generic types
ACDL supports generics in type declarations. A type is generic if it declares one or more type parameters. You write the type parameters within angle brackets (<
and >
) immediately following the name of the generic type. In the following example, Basket<T>
is a generic type and T
is the type parameter.
type Basket<T> {
T inside
Number count
}
The following is another example with an explicit type bound on the type parameter.
type FruitBasket<T : Fruit> {
T fruit
Number count
}
When you use generic types in declarations, you can declare the type parameter specifically, or the compiler can infer the type. In the following example, the compiler validates that Mango
is a subtype of Fruit
. The compiler assigns the type of the expression to fruit
, which is compatible with type Fruit
.
argument = FruitBasket<Mango> {fruit = Mango {...}, count = 2}
ACDL can also infer the type of the type parameter if you don't explicitly specify the type. In the following example, the type parameter T
in the type declaration of FruitBasket
resolves to Mango
because the expression assigned to the fruit
property is of type Mango
.
argument = FruitBasket {fruit = Mango {...}, count = 2}
If there's no way to infer a type parameter, the ACDL compiler sets the parameter to the type bound. In the following example, the type parameter T
resolves to its type bound Thing
.
type Event<T> {
optional T entities
}
argument = Event {} // This is valid, because "entities" is optional property.
Generic type restrictions
Alexa Conversations currently only supports generic types with the types declared in the ACCL. You can't use generic types for types you declare.
List
A List
is a generic with type parameter T
. An expression of type List
declares a list of expressions compatible with type T
. The List
type is declared in the com.amazon.alexa.schema
namespace as follows.
namespace com.amazon.alexa.schema
type List<T> {}
You can declare a type with a List
property. In the following example, the property hobbies
is a List
.
namespace org.example.mydialog
import com.amazon.alexa.schema.String
import com.amazon.alexa.schema.List
type User {
String firstName
String lastName
List<String> hobbies
}
Type compatibility rules
When the ACDL compiler determines the compatibility between two types, T1
and T2
, the following rules apply:
- Types
T1
andT2
are the same when they have exactly the same fully qualified name. - You can assign expressions of type
T1
to names of typeT2
whenT1
andT2
are the same, orT1
is a subtype ofT2
.
Consider the following representations of types A
, B
, and C
, and the action myAction
.
type A {
NameA nameA
}
type B {
NameA nameA
}
type C : A {
NameB nameB
}
action Nothing myAction(A a)
sample {
b = B { nameA = NameA { ... } } // Declares a name of type B
myAction(b) // Not valid because type B is not a subtype of type A
c = C { nameA = NameA { ... }, nameB = NameB { ... } }
myAction(c) // Valid because type C is a subtype of type A
}
Recursive types
ACDL types can be recursive. That is, each property of a type can directly or indirectly refer to the enclosing type itself. The recursive property must be optional, because the recursion can't be infinite.
The following example shows an infinite recursive type.
// Invalid
type Person {
Person father
}
In the previous example, the type Person
has a required property, father
, of type Person
, which makes the type
Person
an infinite recursive type.
The following example shows a finite recursive type.
// Valid
type Person {
optional Person spouse
}
The following example shows indirect recursion.
type NameA {
NameB nameB
}
// This is invalid because NameA has a property of type NameB, and NameB in turn has a property of type NameA,
// which creates an infinite recursion.
type NameB {
NameA nameA
}
Slot types
To use a custom type in an utterances<T>()
action, you must declare the type in the interaction model file. The ACDL compiler automatically generates a type under the slotTypes
namespace for your declared types, and the declared types extend the String
type. You can import your declared types into ACDL files as shown in the slot type example.
The interaction model file doesn't support complex types, so types the ACDL compiler generates from the interaction model file don't have properties.
Slot type example
Consider this interaction-model file segment.
{
"interactionModel": {
...
"types": [
{
"name": "FirstName",
"values": [...]
},
{
"name": "LastName",
"values": [...]
}
]
}
}
For the interaction-model type segment shown in the previous example, the ACDL compiler generates the following type declarations.
namespace slotTypes
type FirstName : String {}
type LastName : String {}
As stated previously, the ACDL compiler creates these types under the slotTypes
namespace. You can import these types into an ACDL file as follows.
namespace com.weatherbot.dialogs
import slotTypes.FirstName
import slotTypes.LastName
For an example of a full interaction model file, see Interaction Model Files for Alexa Conversations.
You must declare the type of the entity you mention in an utterance set in the interaction model file unless it's a built-in type.
namespace com.weatherbot.dialogs
import slotTypes.FirstName // Import types generated from the interaction model.
import slotTypes.LastName
type User { // The User type isn't used in an utterance set sample, so it doesn't need to be declared in the interaction model file.
FirstName firstName
LastName lastName
}
// Because "firstName" and "lastName" are used in an utterance set sample, they must be of types declared in the interaction model file.
getUserEvent = utterances<User>([
"Give me the user whose name is {firstName} {lastName}"
])
action User getUser(FirstName firstName, LastName lastName)
Related topics
Last updated: Nov 27, 2023