Skip to content

Proposal for ATD modules #265

Open
Open
@mjambon

Description

The ATD language currently doesn't have a dedicated syntax for expressing dependencies on external type definitions and implementations. The current mechanisms use the special pseudo-type abstract or use the special wrap constructor. This is all clunky and hard to remember. As a result, users do their best to fit all the definitions in a single .atd file. It's usually fine but in some cases, it's limiting.

Issues being addressed

  • We want to split ATD files into multiple pieces that depend on one another and fit well within the structure of the application. For example, basic types such as date can be defined in A.atd while two other APIs B.atd and C.atd use types defined in A.atd but are otherwise independent of one another.
  • External implementations that are not derived from ATD files should be easier to use and reference. Such implementations are typically opaque data structures but they offer a way to view them as JSON. It could be that they support only JSON export but not import. For example, JSON can be used to dump metadata about an image loaded in memory (see example in the comments).

Proposal

  • Add a top-level import construct that declares which ATD-compatible units the current ATD file depends on, e.g. import basic_types.
  • External types are referenced using a dot notation e.g. basic_types.date.
  • Separate compilation: External types may remain opaque i.e. the code generators atdgen, atdj, atdpy, etc. should not be forced to access external ATD files when generating code for one given ATD file. The existence of an external type or its arity (number of type parameters of a polymorphic type) may not be checked because of this.

Syntax

We introduce a new top-level import construct, which should (must?) occur at the beginning of the ATD file as it will affect all type definitions (since type definitions are reordered based on their mutual dependencies anyway).

import REAL_NAME [as SHORT_NAME]

e.g.

import basic_types as base
import time_and_date

REAL_NAME should be the name of the original ATD file without the .atd extension if such file exists.

Referencing the types they provide must be unambiguous without having access to the external definitions. For that, the type names use the dot notation:

type msg = {
  id: string;
  date: time_and_date.date;
  signature: base.signature;
}

Note that this import semantics is similar to Python's. We don't really need to support the form from MODULE import MEMBER because it can be achieved using type aliases. For shorter type names, we can do this:

import basic_types as base
import time_and_date

type date = time_and_date.date

type msg = {
  id: string;
  date: date;
  signature: base.signature;
}

Mapping to target languages

Each target programming language has a slightly different way of managing program units. It's up to the translator (atdgen, atdpy, ...) to do the right thing and support language-specific ATD annotations. For the OCaml and Python targets (atdgen, atdpy), useful annotations could look like this:

import JSON
  <ocaml module="Yojson.Safe">
  <python module="atdpy_json">

type raw_json = JSON.t

type foo = {
  name: string;
  bar: raw_json;
}

or more directly:

import raw_json
  <ocaml module="Yojson.Safe">
  <python module="atdpy_json">

type foo = {
  name: string;
  bar: raw_json.t;
}

For commonly used external modules such as a JSON module, a code generator can place them in scope automatically. In OCaml for example, atdgen could emit the code open Atdgen_runtime which would make a JSON module directly available. For Python where the convention is for modules to use lowercase and where a json module already exists, we might want the name JSON to map to atdpy_json rather than mapping to JSON (nonstandard) or json (already taken). The goal is to make the use of common or standard external libraries not require ATD annotations. We want the following to just work:

import JSON

type foo = {
  name: string;
  bar: JSON.t;
}

Note that the t represents the type of the main data structure provided by the module as is conventional in OCaml. It's a little nicer than JSON.json or than having to create an alias type json = JSON.json. It's not a requirement imposed by ATD.

So far, all the implementations of an ATD module must provide the same collection of types and functions that work on those types. We could provide a way to express "hey, Python's JSON.t isn't named t but is named json" but it's not clear if it's necessary since many other constraints exist for a module interface to be ATD-compatible. Whoever decides that a cross-language module such as JSON should exist, should also specify the set of types it provides.

Metadata

Assignees

No one assigned

    Labels

    design and planningVarious discussions about the futurework in progressWork started; no guarantee about current activity

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions