A symbol is an object with a unique name.

Sometimes it is necessary to refer to an object by name, so that a chosen name will always identify the same object. Finding or creating an object by name is called "interning" it. A symbol that has a name is called an "interned symbol". However we stretch the boundaries of the symbol idea, by treating all other objects as "uninterned symbols". So that any object could be treated as a symbol.

To prevent name clashing, that introduces unwanted equalities, we employ the system of packages, where each symbol belongs to a package, called its home package. The large system design is leveraged due to the mechanism of symbol importing, where the same symbol could be referenced from different packages (see import and in_package functions, for more information).

Symbol syntax

The read function enables translation of the symbol textual representation to an object. The symbol syntax is designed to be verstatile so it can allow arbitrary sets of characters, to enable support for modeling different knowledge domains. Only two characters has the special meaning for the symbol reader, the : character acts as a separator between the package and the name constituent, and the \] symbol escapes any special treatment of a symbol that follows it (including the [\] itself). When a symbol is read, an absence of the package is treated the same as if the [package] parameter of the [create] function wasn't set, e.g., [read c "x"] is the same as [create c "x"], while an empty package denotes the [keyword] package, e.g., [read c ":x"] is the same as [create ~package:keyword c "x"]. {3 Name equality} The equality of two names is defined by equality of their byte representation. Hence, symbols which differ in register will be treated differently, e.g., [Foo <> foo].

val intern : ?public:bool -> ?desc:string -> ?package:string -> string -> ('a, _) cls -> 'a obj knowledge

intern ?public ?desc ?package name cls interns a symbol in a package.

If a symbol with the given name is already interned in a package, then returns its value, otherwise creates a new object.

If symbol is public then it might be advertised and be accessible during the introspection. It is recommeneded to provide a description string if a symbol is public. Note, a non-public symbol still could be obtained by anyone who knows the name.

If the function is called in the scope of one or more in_package pkg<N>, then the package parameter defaults to pkg, otherwise it defaults to "user". See also the keyword package for the special package that holds constants and keywords.

The desc, package, and name parameters could be arbitrary strings (including empty). Any occurence of the package separator symbol (:) will be escaped and won't be treated as a package/name separator.

val keyword : string

keyword = "keyword" is the special name for the package that contains keywords. Basically, keywords are special kinds of symbols whose meaning is defined by their names and nothing else.

val in_package : string -> (unit -> 'a knowledge) -> 'a knowledge

in_package pkg f makes pkg the default package in f.

Every reference to an unqualified symbol in the scope of the f function will be treated as it is qualified with the package pkg. This function will affect both the reader and the pretty printer, thus in_package "pkg" @@ Obj.repr buf will yield something like #<buf 123>, instead of #<pkg:buf 123>.

val import : ?strict:bool -> ?package:string -> string list -> unit knowledge

import ?strict ?package:p names imports all names into p.

The names elements could be either package names or qualified names. If an element is a package, then all public names from this package are imported into the package p. If an element is a qualified symbol then it is imported into p, even if it is not public in the package from which it is being imported.

If any of the elements of the names list doesn't represent a known package or known symbol, then a conflict is raised, either Not_a_package or Not_a_symbol.

If strict is true then no name can change its value during the import. Otherwise, if the name is alredy in present in package p with a different value, then it will be overwritten with the new value, i.e., shadowed.

All names are processed in order, so names imported from packages that are in the beginning of the list could be shadowed by the names that are in the end of the list (unless strict is true, of course). Thus,

import [x] >>= fun () ->
import [y]

is the same as import [x;y].

Note, all imported names are added as not public.

If the package parameter is not specified, then names are imported into the current package, as set by the in_package function.