Knowledge.Symbol
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).
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.
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.
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 set_package : string -> unit knowledge
set_package pkg
makes the package named pkg
the current package.
val package : string knowledge
package
is the name of the current package.
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.