# LispKit Enum

Library `(lispkit enum)` implements an API for enumeration types, enumeration values, and enumeration sets.

An enumeration type is defined by a list of tagged enumeration names. It encapsulates enumeration values which can be accesses either by name or ordinal value. Sets of these enumeration values are called enumeration sets. Each enumeration set is based on an enumeration type and contains a set of enumeration values.

## Declarative API

(define-enum type-name (symbol …) constructor) [syntax]
(define-enumeration type-name (symbol …) constructor) [syntax]

The `define-enum` and `define-enumeration` forms defines an enumeration type and provide two macros for constructing its members and sets of its members.

type-name is an identifier that is bound as a syntactic keyword; symbol … are the symbols that comprise the universe of the enumeration (in order).

`(`type-name symbol`)` checks whether the name of symbol is in the universe associated with type-name. If it is, `(`type-name symbol`)` is equivalent to symbol. It is a syntax violation if it is not. `(`type-name`)` returns the type of the enumeration.

constructor is an identifier that is bound to a syntactic form that, given any finite sequence of the symbols in the universe, possibly with duplicates, expands into an expression that evaluates to the enumeration set of those symbols.

`(`constructor symbol …`)` checks whether every symbol … is in the universe associated with type-name. It is a syntax violation if one or more is not. Otherwise `(`constructor symbol …`)` is equivalent to `((enum-set-constructor (`constructor-syntax`)) '(`symbol …`))`.

Here is a complete example:

    (define-enumeration color
      (black white purple maroon)
      color-set)
    (color black)                 ⇒ black
    (color purpel)                ⇒ error: symbol not in enumeration universe
    (enum-set->list (color-set))  ⇒ ()
    (enum-set->list
      (color-set maroon white))   ⇒ (white maroon)

## Enum types

(enum-type? obj) [procedure]

Returns `#t` if obj is an enum type, and `#f` otherwise.

(make-enum-type list) [procedure]
(make-enum-type name list)
(make-enum-type name list tag)

Returns a newly allocated enum type containing a fixed set of newly allocated enums. Both enums and enum types are immutable, and it is not possible to create an enum except as part of creating an enum type. name is the name of the enumeration as a string or symbol, tag is an arbitrary object attached to the enum type (which can be accessed via `enum-type-tag`).

The elements of list are either symbols or two-element lists, where each list has a symbol as the first element and any value as the second element (this is the enum’s tag). Each list element causes a single enum to be generated, and the enum’s name is specified by the symbol. It is an error unless all the symbols are distinct within an enum type. The position of the element in list is the ordinal of the corresponding enum, so ordinals within an enum type are also distinct. If a value is given, it becomes the value of the enum; otherwise the enum’s value is the same as the ordinal.

Here are a few examples:

    (define color
      (make-enum-type
        '(red orange yellow green cyan blue violet)))

    (define us-traffic-light
      (make-enum-type '(red yellow green)))

    (define pizza
      (make-enum-type
        '((margherita "tomato and mozzarella")
          (funghi "mushrooms")
          (chicago "deep-dish")
          (hawaiian "pineapple and ham"))))

(enum-type-type-tag enum-type) [procedure]

Returns the type tag, i.e. an uninterned symbol, representing the type of enums as defined by enum-type.

(enum-type-size enum-type) [procedure]

Returns an exact integer equal to the number of enums in enum-type.

(enum-min enum-type) [procedure]

Returns the enum belonging to enum-type whose ordinal is 0.

(enum-max enum-type) [procedure]

Returns the enum belonging to enum-type whose ordinal is equal to the number of enums in the enum type minus 1.

(enum-type-tag enum-type) [procedure]

Returns the tag associated with enum-type.

(enum-type-enums enum-type) [procedure]

Returns a list of the enums belonging to enum-type ordered by increasing ordinal.

(enum-type-names enum-type) [procedure]

Returns a list of the names of the enums belonging to enum-type ordered by increasing ordinal.

(enum-type-tags enum-type) [procedure]

Returns a list of the values of the enums belonging to enum-type ordered by increasing ordinal.

(enum-type-contains? enum-type enum) [procedure]

Returns `#t` if enum belongs to enum-type, and `#f` otherwise.

    (enum-type-contains? color
      (enum-name->enum color 'red)) ⇒ #t
    (enum-type-contains? pizza
      (enum-name->enum color 'red)) ⇒ #f

(enum-type-test-predicate enum-type) [procedure]

Returns a procedure which given an object, checks that this object is an enum that belongs to enum-type.

(enum-set-type-test-predicate enum-type) [procedure]

Returns a procedure which given an object, checks that this object is an enum set whose type matches enum-type.

(enum-name->enum enum-type symbol) [procedure]

If there exists an enum belonging to enum-type named symbol, returns it; otherwise return `#f`.

(enum-name->ordinal enum-type symbol) [procedure]

Returns the ordinal of the enum belonging to enum-type whose name is symbol. It is an error if there is no such enum.

(enum-name->tag enum-type symbol) [procedure]

Returns the value of the enum belonging to enum-type whose name is symbol. It is an error if there is no such enum.

(enum-ordinal->enum enum-type exact-integer) [procedure]

If there exists an enum belonging to enum-type whose ordinal is exact-integer, returns it; otherwise return `#f`.

(enum-ordinal->name enum-type exact-integer) [procedure]

Returns the name of the enum belonging to enum-type whose ordinal is exact-integer. It is an error if there is no such enum.

(enum-ordinal->tag enum-type exact-integer) [procedure]

Returns the value of the enum belonging to enum-type whose ordinal is exact-integer. It is an error if there is no such enum.

(enum-tag-mapper enum-type) [procedure]
(enum-tag-mapper enum-type makeht)
(enum-tag-mapper enum-type hash equal)

Returns a procedure that maps enum tags to enum values for the given enumeration type enum-type. The returned procedure accepts a tag and returns the corresponding enum value, or `#f` if not found. makeht is an optional procedure that creates a hashtable from an association list (defaults to `alist->equal-hashtable`). Alternatively, hash and equal procedures can be provided to create a custom hashtable for the mapping.

(enum-type->enum-set enum-type) [procedure]

Returns an enum set containing all enum values from enumeration type enum-type.

## Enum values

(enum? obj) [procedure]

Returns `#t` if obj is an enum, and `#f` otherwise.

(enum-type enum) [procedure]

Returns the enum type to which enum belongs.

(enum-name enum) [procedure]

Returns the name (symbol) associated with enum.

(enum-ordinal enum) [procedure]

Returns the ordinal (exact integer) associated with enum.

(enum-tag enum) [procedure]

Returns the tag associated with enum.

(enum-next enum) [procedure]

Returns the enum that belongs to the same enum type as enum and has an ordinal one greater than enum. Returns `#f` if there is no such enum.

    (enum-name (enum-next color-red)) ⇒ orange
    (enum-next (enum-max color))      ⇒ #f

(enum-prev enum) [procedure]

Returns the enum that belongs to the same enum type as enum and has an ordinal one less than enum. Returns `#f` if there is no such enum.

(enum=? enum0 enum1 …) [procedure]

Returns `#t` if all the arguments are the same enum in the sense of `eq?` (which is equivalent to having the same name and ordinal) and `#f` otherwise. It is an error to apply `enum=?` to enums belonging to different enum types.

    (enum=? color-red color-blue)      ⇒ #f
    (enum=? pizza-funghi
      (enum-name->enum pizza 'funghi)) ⇒ #t
    (enum=? color-red
      (enum-name->enum color 'red)
      color-blue)                      ⇒ #f

(enum<? enum0 enum1 …) [procedure]
(enum>? enum0 enum1 …) [procedure]
(enum<=? enum0 enum1 …) [procedure]
(enum>=? enum0 enum1 …) [procedure]

These predicates return `#t` if their arguments are enums whose ordinals are in increasing, decreasing, non-decreasing, and non-increasing order respectively, and `#f` otherwise. It is an error unless all of the arguments belong to the same enum type.

## Enum sets

(enum-set? obj) [procedure]

Returns `#t` if obj is an enum set and `#f` otherwise.

(enum-set enum-type enum …) [procedure]

Returns an enum set that can contain enums of the type enum-type and containing the enums. It is an error unless all the enums belong to enum-type.

    (enum-set-contains?
      (enum-set color color-red color-blue)
      color-red)     ⇒  #t
    (enum-set-contains?
      (enum-set color color-red color-blue)
      color-orange)  ⇒  #f

(list->enum-set enum-type list) [procedure]

Returns an enum set with the specified enum-type that contains the members of list. list may contain enums, enum names, or enum ordinals. It is an error unless all the members refer to enums belonging to enum-type.

(enum-set-copy enum-set) [procedure]

Returns a copy of enum-set.

(enum-set-empty? enum-set) [procedure]

Returns `#t` if enum-set is empty, and `#f` otherwise.

(enum-set-contains? enum-set enum) [procedure]

Returns `#t` if enum is a member of enum-set. It is an error if enum does not belong to the same enum type as the members of enum-set.

(enum-set-disjoint? enum-set1 enum-set2) [procedure]

Returns `#t` if enum-set1 and enum-set2 do not have any enum objects in common, and `#f` otherwise.

    (define reddish
      (list->enum-set
        (map (lambda (name) (enum-name->enum color name))
             '(red orange))))
    (define ~reddish
      (list->enum-set
        (map (lambda (name) (enum-name->enum color name))
             '(yellow green cyan blue violet))))
    (enum-set-disjoint? color-set reddish) ⇒ #f
    (enum-set-disjoint? reddish ~reddish)  ⇒ #t

(enum-set-projection enum-set1 enum-set2) [procedure]

Projects enum-set1 into the universe of enum-set2, dropping any elements of enum-set1 that do not belong to the universe of enum-set2. If enum-set1 is a subset of the universe of its second, no elements are dropped, and the injection is returned.

    (let ((e1 (make-enumeration '(red green blue black)))
          (e2 (make-enumeration '(red black white))))
      (enum-set->list (enum-set-projection e1 e2))))
    ⇒ (red black)

(enum-set-member? symbol enum-set) [procedure]
(enum-set-subset? enum-set1 enum-set2) [procedure]

The `enum-set-member?` procedure returns `#t` if its first argument is an element of its second argument, `#f` otherwise.

The `enum-set-subset?` procedure returns `#t` if the universe of enum-set1 is a subset of the universe of enum-set2 (considered as sets of symbols) and every element of enum-set1 is a member of enum-set2. It returns `#f` otherwise.

    (let* ((e (make-enumeration '(red green blue)))
           (c (enum-set-constructor e)))
      (list (enum-set-member? 'blue (c '(red blue)))
            (enum-set-member? 'green (c '(red blue)))
            (enum-set-subset? (c '(red blue)) e)
            (enum-set-subset? (c '(red blue)) (c '(blue red)))
            (enum-set-subset? (c '(red blue)) (c '(red)))
            (enum-set=? (c '(red blue)) (c '(blue red)))))
    ⇒ (#t #f #t #t #f #t)

(enum-set=? enum-set1 enum-set2) [procedure]

Returns `#t` if the members of enum-set-1 are the same as of enum-set-2. It is an error if the members of the enum sets do not belong to the same type.

(enum-set<? enum-set1 enum-set2) [procedure]

Returns `#t` if the members of enum-set-1 are a proper subset of enum-set-2. It is an error if the members of the enum sets do not belong to the same type.

(enum-set>? enum-set1 enum-set2) [procedure]

Returns `#t` if the members of enum-set-1 are a proper superset of enum-set-2. It is an error if the members of the enum sets do not belong to the same type.

(enum-set<=? enum-set1 enum-set2) [procedure]

Returns `#t` if the members of enum-set-1 are a subset of enum-set-2. It is an error if the members of the enum sets do not belong to the same type.

(enum-set>=? enum-set1 enum-set2) [procedure]

Returns `#t` if the members of enum-set-1 are a superset of enum-set-2. It is an error if the members of the enum sets do not belong to the same type.

(enum-set->list enum-set) [procedure]

Returns a list of the names of enums that belong to enum-set. The list is in increasing order of the enums.

    (let* ((e (make-enumeration '(red green blue)))
           (c (enum-set-constructor e)))
      (enum-set->list (c '(blue red))))
    ⇒ (red blue)

(enum-set->enum-list enum-set) [procedure]

Returns a list containing the enum members of enum-set. The list is in increasing order of the enums.

    (let* ((e (make-enumeration '(red green blue)))
           (c (enum-set-constructor e)))
      (enum-set->enum-list (c '(blue red))))
    ⇒ (#<enum enum-3: 0> #<enum enum-3: 2>)

(enum-set-next enum-set e) [procedure]

Returns the ordinally next enum in enum-set following enum e. e is either a name, ordinal, or enum value. `enum-set-next` returns `#f` if there is no next enum.

(enum-set-type enum-set) [procedure]

Returns the enum type associated with enum-set.

(enum-set-bitset enum-set) [procedure]

Returns a bit set (as defined by library `(lispkit bitset)`) representing all ordinals of enum-set.

(enum-set-size enum-set) [procedure]

Returns the number of elements in enum-set.

(enum-set-adjoin! enum-set e …) [procedure]

Adds enums e … to enum-set. Enums are defined either via a name, an ordinal, or an enum object. It is an error if the enums denoted by e … do not all belong to the same enum type.

(enum-set-adjoin-all! enum-set list) [procedure]

list is a list of enums. Enums are defined either via a name, an ordinal, or an enum object. `enum-set-adjoin-all!` adds all enums of list to enum-set. It is an error if the enums denoted by list do not all belong to the same enum type.

(enum-set-delete! enum-set e …) [procedure]

Removes enums e … from enum-set. Enums are defined either via a name, an ordinal, or an enum object. It is an error if the enums denoted by e … do not all belong to the same enum type.

(enum-set-delete-all! enum-set list) [procedure]

list is a list of enums. Enums are defined either via a name, an ordinal, or an enum object. `enum-set-delete-all!` removes all enums of list from enum-set. It is an error if the enums denoted by list do not all belong to the same enum type.

(enum-set-union enum-set1 enum-set2) [procedure]
(enum-set-intersection enum-set1 enum-set2) [procedure]
(enum-set-difference enum-set1 enum-set2) [procedure]

Arguments enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.

The `enum-set-union` procedure returns the union of enum-set1 and enum-set2. The `enum-set-intersection` procedure returns the intersection of enum-set1 and enum-set2. The `enum-set-difference` procedure returns the difference of enum-set1 and enum-set2.

    (let* ((e (make-enumeration '(red green blue)))
           (c (enum-set-constructor e)))
      (list (enum-set->list (enum-set-union (c '(blue)) (c '(red))))
            (enum-set->list
              (enum-set-intersection (c '(red green)) (c '(red blue))))
            (enum-set->list
             (enum-set-difference (c '(red green)) (c '(red blue))))))
    ⇒ ((red blue) (red) (green))

(enum-set-xor enum-set1 enum-set2) [procedure]

Procedure `enum-set-xor` returns the exclusive disjunction of enum-set1 and enum-set2. Arguments enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.

(enum-set-complement enum-set) [procedure]

Returns enum-set’s complement with respect to its universe.

    (let* ((e (make-enumeration '(red green blue)))
           (c (enum-set-constructor e)))
      (enum-set->list (enum-set-complement (c '(red)))))
    ⇒ (green blue)

(enum-set-union! enum-set1 enum-set2) [procedure]

Creates the union of enum-set1 and enum-set2 and stores its result in enum-set1. enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.

(enum-set-intersection! enum-set1 enum-set2) [procedure]

Creates the intersection of enum-set1 and enum-set2 and stores its result in enum-set1. enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.

(enum-set-difference! enum-set1 enum-set2) [procedure]

Creates the set difference between enum-set1 and enum-set2 and stores its result in enum-set1. enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.

(enum-set-xor! enum-set1 enum-set2) [procedure]

Creates the exclusive disjunction between enum-set1 and enum-set2 and stores its result in enum-set1. enum-set1 and enum-set2 must be enumeration sets that have the same enumeration type.

(enum-set-complement! enum-set) [procedure]

Replaces enum-set with its complement with respect to the type of enum-set.

(enum-set-indexer enum-set) [procedure]

Returns a unary procedure that, given a symbol that is in the universe of enum-set, returns its 0-origin index within the canonical ordering of the symbols in the universe; given a value not in the universe, the unary procedure returns `#f`.

    (let* ((e (make-enumeration '(red green blue)))
           (i (enum-set-indexer e)))
      (list (i 'red) (i 'green) (i 'blue) (i 'yellow)))
     ⇒ (0 1 2 #f)

The `enum-set-indexer` procedure could be defined as follows using the `memq` procedure:

    (define (enum-set-indexer set)
      (let* ((symbols (enum-set->list (enum-set-universe set)))
             (cardinality (length symbols)))
        (lambda (x)
          (cond ((memq x symbols) =>
                  (lambda (probe) (- cardinality (length probe))))
                (else #f)))))

(enum-set-any? pred enum-set) [procedure]

Returns `#t` if any application of pred to the elements of enum-set returns true, and `#f` otherwise.

(enum-set-every? pred enum-set) [procedure]

Returns `#t` if every application of pred to the elements of enum-set returns true, and `#f` otherwise.

(enum-set-count pred enum-set) [procedure]

Returns an exact integer, the number of elements of enum-set that satisfy pred.

(enum-set-map->list proc enum-set) [procedure]

Invokes proc on each member of enum-set in increasing ordinal order. The results are returned as a list.

(enum-set-for-each proc enum-set) [procedure]

Invokes proc on each member of enum-set in increasing ordinal order and discards the rest.

(enum-set-fold proc nil enum-set) [procedure]

The current state is initialized to nil, and proc is invoked on each element of enum-set in increasing ordinal order and the current state, setting the current state to the result. The algorithm is repeated until all the elements of enum-set have been processed. Then the current state is returned.

(enum-set-filter pred enum-set) [procedure]

Returns an enum set containing the enums in enum-set that satisfy pred.

(enum-set-remove pred enum-set) [procedure]

Returns an enum set containing the enums in enum-set that do not satisfy pred.

## R6RS Compatibility

(make-enumeration symbol-list) [procedure]

Argument symbol-list must be a list of symbols. The `make-enumeration` procedure creates a new enumeration type whose universe consists of those symbols (in canonical order of their first appearance in the list) and returns that universe as an enumeration set whose universe is itself and whose enumeration type is the newly created enumeration type.

(enum-set-universe enum-set) [procedure]

Returns the set of all symbols that comprise the universe of its argument enum-set, as an enumeration set.

(enum-set-constructor enum-set) [procedure]

Returns a unary procedure that, given a list of symbols that belong to the universe of enum-set, returns a subset of that universe that contains exactly the symbols in the list. The values in the list must all belong to the universe.

(enum-constructor enum-set) [procedure]

Given an enum set, `enum-constructor` returns a procedure which takes an enum name and returns the corresponding enum object.