Names in the Alexa Conversations Description Language

In Alexa Conversations Description Language (ACDL), you can declare names for expressions. Name declarations let you reuse expressions in the same or different ACDL files by referring to the declared name, instead of repeating the corresponding expression.

The examples on this page use the following Person type declaration.

type Person {
  String firstName
  String lastName
}

Name declaration

Action declarations, type declarations, dialog declarations, and named expressions are all name declarations. In other words, you give them a name when you make the declaration.

The name of the following action declaration is getWeather.

action WeatherResult getWeather(US_CITY cityName, DATE, date)

The name of the following type declaration is Person.

type Person {
  String firstName
  String lastName
}

The name of the following dialog declaration is Weather.

dialog Nothing Weather {
  ...
}

Named expressions

You declare a new name with <name> = <expression> syntax. You must declare the name without a namespace qualifier, because the name is always associated with the namespace declared in the corresponding file.

Valid expressions

A name declaration is valid if the name conforms to the rules applicable to an identifier, and if the expression is valid. If the declaration is valid, the declared name is of the same type as the expression used in the declaration. The following example shows you how to give the name jane to an expression that represents a Person. In other words, you declare the name jane to be of the type Person.

dialog Nothing MyDialog {
  sample {
    jane = Person { firstName = "Jane", lastName = "Doe" }
    myAction(jane)
  }
}

Invalid expressions

The following example declares the name jane. This declaration, however, is part of an invalid expression. It's invalid because the Person type declaration has two required properties, firstName and lastName, neither of which is included in this expression. To correct the error, you must supply both required Person properties.

dialog Nothing MyDialog {
  sample {
    jane = Person {} // This is invalid.
  }
}

You can't declare a name with an expression of type Nothing. In the following example, nothing is a literal and is the only possible value of type Nothing.

myName = nothing // This is invalid

You also can't declare names for expressions that return the Nothing type, as shown in the following example.

action Nothing setUserCityPreference(US_CITY userCity)

dialog MyDialog {
  sample {
    ...
    result = setUserCityPreference(cityName) // Invalid because setUserCityPreference returns Nothing
    ...
  }
}

Name equivalents

When you use a previously declared name as the expression in a new name declaration, you essentially declare another name for the original expression. In the following example, both lane and jane are names of the same Person expression.

dialog Nothing MyDialog {
  sample {
    jane = Person { firstName = "Jane", lastName = "Doe" }
    lane = jane
    // lane and jane are names of the same Person expression.
  }
}

The following example is equivalent to the previous example. That is, lane and jane are names for the same expression.

dialog Nothing MyDialog {
  sample {
    jane = Person { firstName = "Jane", lastName = "Doe" }
    lane = Person { firstName = "Jane", lastName = "Doe" }
  }
}

Fully qualified names

A fully qualified name is one that includes its namespace qualifier as a prefix. In other words, the name's prefix is the name of the namespace that contains the declaration.

The following example shows how to use a fully qualified name.

namespace org.example.mydialog

sample {
  //org.example.actions.getFirstName is a fully qualified name and can be used without an import declaration
  firstName = org.example.actions.getFirstName()
}

Compare the preceding example to the following one, which uses an import statement instead of a fully qualified name.

namespace org.example.mydialog

import org.example.actions.getFirstName

sample {
  //getFirstName is imported above, so it can be referenced without the fully qualified name.
  firstName = getFirstName()
}

In an ACDL file, you need not include an import statement if you use a fully qualified name that refers to declarations from other namespaces. Fully qualified names have a maximum length of 64 characters.

Scope

You declare every name within a scope. The visibility of each name (that is, whether it can appear in an expression) depends on the scope in which you declare it. The following list shows the available scopes.

Namespace scope

The namespace scope is the top-level scope. If you declare a name at the top level, you can reference that name throughout any file in that namespace without using the namespace qualifier. If you declare a name within a narrower (block) scope, however, you can't reference the name outside the block.

In the following example, weatherEvent is declared within a namespace scope. You can reference weatherEvent elsewhere in the same namespace scope without a namespace qualifier. In other namespace scopes, you can reference it only by its fully qualified name or by using import.

namespace com.weatherbot.dialogs

import com.amazon.alexa.conversations.expect

weatherEvent = utterances(..)

dialog Nothing Weather {
  sample {
    wr = expect(Invoke, weatherEvent)
  }
}

In the following example, weatherEvent is declared in the com.weatherbot.samples namespace. In the com.weatherbot.dialogs namespace, it's referenced by its fully qualified name, com.weatherbot.samples.weatherEvent.

//file: WeatherSamples.acdl
namespace com.weatherbot.samples

import com.amazon.alexa.conversations.utterances

weatherEvent = utterances(..)

//file: Weather.acdl
namespace com.weatherbot.dialogs

import com.amazon.alexa.conversations.expect

dialog Nothing Weather {
  sample {
    wr = expect(Invoke, com.weatherbot.samples.weatherEvent)
  }
}

The order in which you declare names in a given namespace scope doesn't matter. In the following example, nameB and nameA are declared in the same namespace scope. The declarations are valid, then, even though nameB is declared before nameA.

namespace com.weatherbot.dialogs

nameB = nameA
nameA = "hello"

Block scope

A block scope spans the set of expressions declared within an expression block.

When you declare a name inside a block scope, the name is visible from the point at which you declare it through the end of the block. In addition, a block scope inherits names declared at a higher level. The namespace scope is the top-level scope, so all blocks inherit the names declared at that level.

In the following example, weatherResponse is scoped to the namespace, and weatherResult is scoped to the current block (the sample).

namespace com.weatherbot.dialogs

import com.amazon.alexa.conversations.response
import com.amazon.alexa.conversations.MultiModalResponse

weatherResponse = MultiModalResponse {apla = weatherPrompt}

dialog Nothing Weather {
  sample {
    weatherResult = getWeather(...)
    response(weatherResponse, Notify {actionName = getWeather}, payload = WeatherPayload {result = weatherResult})
  }
}

Names declared within a block scope aren't visible outside that scope. In the following example, both declarations of weatherResult are visible within their respective blocks. However, weatherResult is invalid when used in the sample block that contains the declarations.

namespace com.weatherbot.dialogs

dialog Nothing Weather {
  sample {
    if (condition) {
      weatherResult = getWeatherWithRainInfo(...)
    } else {
      weatherResult = getWeather(...)
    }
    result = weatherResult // Invalid because weatherResult is used outside the block where it's declared
  }
}

The following valid example shows how to use weatherResult within each block scope.

namespace com.weatherbot.dialogs

dialog Nothing Weather {
  sample {
    ...
    if (condition) {
      weatherResult = getWeatherWithRainInfo(...)
      result = weatherResult
               ...
    } else {
      weatherResult = getWeather(...)
      result = weatherResult
      ..
    }
  }
}

Name redeclaration

You can't redeclare a name with the same scope or a narrower scope. The following example is invalid because weatherResponse already exists within the namespace scope.

namespace com.weatherbot.dialogs

import com.amazon.alexa.conversations.MultiModalResponse

weatherResponse = MultiModalResponse {...}

dialog Nothing Weather {
  sample {
    ...
    weatherResponse = MultiModalResponse {...} // Invalid because weatherResponse already exists within the namespace scope.
    ...
  }
}

Similarly, the following example is invalid because it redeclares the weatherResponse imported from the namespace com.weatherbot.responses.

namespace com.weatherbot.dialogs

import com.weatherbot.responses.weatherResponse
import com.amazon.alexa.conversations.response

dialog Nothing Weather {
  sample {
    ...
    weatherResponse = MultiModalResponse {...} // Invalid because weatherResponse is imported from another namespace
    response(weatherResponse, ...)
  }
}

Circular dependencies

ACDL doesn't allow circular dependencies between declarations. The compiler flags any circular dependencies as errors. You can make declarations out of order in ACDL, but this flexibility can produce unexpected circular dependencies between declarations in a given namespace.

A circular dependency can be direct or indirect. The following example shows a direct circular dependency (nameA -> nameB -> nameA).

namespace org.example.mydialog

// This is invalid.
nameA = nameB
nameB = nameA

The following example shows an indirect circular dependency (nameA -> nameB -> nameC -> nameA).

namespace org.example.mydialog

// This is invalid.
nameA = nameB
nameB = nameC
nameC = nameA