On this page:
tagged
tagged?
define-tagged
tagged-supertype
tagged-supertype*
Tagged  Top
Tagged  Top?
uniform-get
7.1

1 Tagged structures

Tagged structures behave like Racket’s plain structs, but do not need to be declared before they are used. They are similar to prefab structs in that aspect, but prefab structs lack field names, i.e. they behave more like vectors tagged with the prefab struct’s name.

A tagged structure is identified by its tag name, and its set of field names. The type of a tagged structure can be expressed without having declared the tagged structure in advance. It is also possible to create instances of tagged structures without declaring them beforehand, and this applies to match patterns for tagged structures too. Fields can be accessed without knowing the structure’s tag name, using (uniform-get instance field-name).

These features make tagged structures particularly suited for writing compilers and other programs which transform large and complex data structures in small steps. This library is designed to work hand in hand with the phc-graph library (not available yet, but will be soon), which adds to tagged structures some support for safe cyclic data structures, and easy manipulation of those via higher-order operations. The regular tagged structures should normally not be used to form cyclic data structuresIt is possible in theory to build cyclic data structures using shared, for example, but this use case is not supported by this library, and is unlikely to play well with Typed/Racket in any case.. Thus, the graph library uses nodes instead, which can contain cycles, as long as the cycles are safely created via the graph library. Nodes also have a tag name and a set of fields, and each node type is a subtype of the corresponding tagged structure with the same name and fields.

type expander

(tagged tag-name fields-maybe-types)

 
tag-name = Identifier
     
fields-maybe-types = just-fieldᵢ ...
  | maybe-∀ field+type-descriptorᵢ ...
     
maybe-∀ = 
  | #:∀ (tvarⱼ ...)
     
just-fieldᵢ = fieldᵢ
  | [fieldᵢ]
     
field+type-descriptor = [fieldᵢ typeᵢ]
  | [fieldᵢ : typeᵢ]
     
fieldᵢ = Identifier
     
typeᵢ = Type
     
tvarⱼ = Identifier
Expands to the type for a tagged structure with the given tag name and fields. If the types for the fields are not given, then the tagged structure is polymorphic, with one type variable per field.

If #:∀ (tvarⱼ ...) is specified, a polymorphic tagged structure is polymorphic, with the given type variables.

The elements may appear in any order, as long as the tag name appears before any field descriptor, and as long as the field descriptors form a contiguous sequence.

syntax

(tagged maybe-instance maybe-∀ tag-name fields-maybe-types)

(tagged maybe-builder  maybe-∀ tag-name fields+values-maybe-types)
 
maybe-instance = 
  | #:instance
     
maybe-builder = 
  | #:builder
     
tag-name = Identifier
     
maybe-∀ = 
  | #:∀ (tvarⱼ ...)
     
fields-maybe-types = just-fieldᵢ ...
  | [fieldᵢ : typeᵢ] ...
     
just-fieldᵢ = fieldᵢ
  | [fieldᵢ]
     
field+value-maybe-type = [fieldᵢ valueᵢ] ...
  | field+value+typeᵢ ...
     
field+value+typeᵢ = [fieldᵢ : typeᵢ valueᵢ]
  | [fieldᵢ valueᵢ : typeᵢ]
     
fieldᵢ = Identifier
     
typeᵢ = Type
     
valueᵢ = Expression
When using the fields-maybe-types syntax, this form expands to a lambda function which can be used to build an instance of the tagged structure, by passing as many values as there are fields.

When using the fields+values-maybe-types syntax, this form directly returns an instance of the tagged structure, with the given values.

It is mandatory to disambiguate with either #:instance or #:builder when using tagged with an empty list of fields (i.e. a structure with no fields) as it cannot be guessed from the syntax otherwise, so it is best to always include one or the other when writing a macro which expands to uses of tagged.

When types are specified, they are used to annotate the values when producing an instance, otherwise they are used as the argument types for the builder function.

When #:∀ (tvarⱼ ...) is specified for a builder, a polymorphic builder is produced, with the given tvarⱼ ... type variables.

When #:∀ (tvarⱼ ...) is specified for an instance, the type of values annotated with tvarⱼ is inferred, and an instance of a polymorphic tagged structure is produced. A tvarⱼ can be used within a more complex type, in which case only that part of the type is inferred.

The elements may appear in any order, as long as the tag name appears before any field descriptor, and as long as the field descriptors form a contiguous sequence.

match expander

(tagged tag-name maybe-no-implicit field-maybe-patsᵢ ...)

 
tag-name = Identifier
     
maybe-no-implicit = 
  | #:no-implicit-bind
     
field-maybe-patsᵢ = fieldᵢ
  | [fieldᵢ patᵢⱼ ...]
     
fieldᵢ = Identifier
     
patᵢⱼ = "match pattern"
Expands to a match pattern for a tagged structure with the given name and fields. The value of each fieldᵢ is matched against all of the corresponding patᵢⱼ .... When there are not patᵢⱼ for a fieldᵢ, the brackets around the field name may be omitted.

Unless #:no-implicit-bind is specified, every fieldᵢ is bound by the match pattern to the field’s value.

The elements may appear in any order, as long as the tag name appears before any field-maybe-patsᵢ, and as long as the field-maybe-patsᵢ form a contiguous sequence.

syntax

(tagged? tag-name fieldᵢ ...)

(tagged? tag-name [fieldᵢ : typeᵢ] ...)
(tagged? tag-name [fieldᵢ predᵢ] ...)
 
  tag-name : Identifier
  fieldᵢ : Identifier
  typeᵢ : Type/Make-Predicate
  predᵢ : (ExpressionOf ( Any Any : typeᵢ))
Expands to a predicate for tagged structures with the given tag and fields. If types are specified, each typeᵢ is passed to make-predicate, and the resulting predicate is checked against the value of the corresponding fieldᵢ.

Each typeᵢ must therefore be a valid type for which make-predicate can generate a predicate (make-predicate cannot create a predicate for some types, like function types, or any type which translates to a chaperone contract.

The last form allows the use of arbitrary predicates predᵢ which are checked against the value of the corresponding fieldᵢ. When the type of a given predᵢ includes a filter asserting that it returns true if and only if the value is of type typeᵢ, then the predicate produced by tagged-predicate! will also have that filter on the corresponding field. By default, any function of type ( Any Any) will implicitly have the Any filter, which does not bring any extra information.

The elements may appear in any order, as long as the tag name appears before any field descriptor, and as long as the field descriptors form a contiguous sequence.

syntax

(define-tagged name maybe-tag-name maybe-predicate? fields-maybe-types)

 
name = Identifier
     
maybe-tag-name = 
  | #:tag tag-name
     
tag-name = Identifier
     
maybe-predicate? = 
  | #:? predicate-name?
     
predicate-name? = Identifier
     
fields-maybe-types = just-fieldᵢ ...
  | maybe-∀ field+type-descriptorᵢ ...
     
maybe-∀ = 
  | #:∀ (tvarⱼ ...)
     
just-fieldᵢ = fieldᵢ
  | [fieldᵢ]
     
field+type-descriptor = [fieldᵢ typeᵢ]
  | [fieldᵢ : typeᵢ]
     
fieldᵢ = Identifier
     
typeᵢ = Type
     
tvarⱼ = Identifier
Defines name as a shorthand for the type expander, match expander, builder function and predicate for a tagged structure with the given tag-name and fields.

When #:tag tag-name is omitted, it defaults to name.

The predicate is bound to predicate-name?; When #:? predicate-name? is omitted, it defaults to name?, which is an identifier with the same lexical context as name, with a "?" appended at the end.

The name and predicate? identifiers behave as follows:

type expander

name

Expands to the same type as (tagged tag-name fields-maybe-types) would.

syntax

name

Expands to the same builder function as (tagged #:builder tag-name fields-maybe-types) would.

match expander

(name patᵢ ...)

Expands to the same match pattern as (tagged tag-name [fieldᵢ patᵢ] ...) would.

syntax

predicate?

Expands to the same predicate as (tagged? just-fieldᵢ) would. Note that it does not attempt to check the field values, as doing so would mean that all of the typeᵢ, if specified, would have to be suitable arguments for make-predicate.

The elements of the grammar for define-tagged may appear in any order, as long as the tag name appears before any field descriptor, and as long as the field descriptors form a contiguous sequence.

type expander

(tagged-supertype tag-name field+typeᵢ ...)

 
tag-name = Identifier
     
field+type = [fieldᵢ typeᵢ]
  | [fieldᵢ : typeᵢ]
Expands to the union type of all tagged structures with the given name and a superset of the given fields, where each given fieldᵢ must have the corresponding type typeᵢ, and the other fields have the type Any.

match expander

(tagged-supertype tag-name maybe-no-implicit field-maybe-patsᵢ ...)

 
tag-name = Identifier
     
maybe-no-implicit = 
  | #:no-implicit-bind
     
field-maybe-patsᵢ = fieldᵢ
  | [fieldᵢ patᵢⱼ ...]
     
fieldᵢ = Identifier
     
patᵢⱼ = "match pattern"
Expands to a match pattern accepting any tagged structures with the given name and a superset of the given fields, where each given fieldᵢ must match all the corresponding patᵢⱼ, and the other fields are matched against _ (i.e. they can contain any value).

Unless #:no-implicit-bind is specified, every fieldᵢ is bound by the match pattern to the field’s value, but the other extra fields are not bound to any variable.

The elements may appear in any order, as long as the tag name appears before any field descriptor, and as long as the field descriptors form a contiguous sequence.

syntax

(tagged-supertype* )

Currently not implemented. Will be equivalent to nesting tagged-supertype.

The supertype of all tagged structures, including untagged structures, nodes and constructors.

procedure

(TaggedTop? v)  Boolean

  v : Any
A predicate for TaggedTop. It accepts all tagged structures, including untagged structures, nodes and constructors, and rejects any other value.

syntax

(uniform-get v f)

 
v = Expression
     
f = Identifier
Returns the value contained within the f field of the tagged structure instance v.