Types in the Alexa Conversations Description Language (ACDL)

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

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`

Declaring 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.

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 is 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 and T2 are the same when they have exactly the same fully qualified name.
  • You can assign expressions of type T1 to names of type T2 when T1 and T2 are the same, or T1 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 is an example of 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 is an example of a finite recursive type.

// Valid
type Person {
  optional Person spouse
}

The following is an example of 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 is 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)