Domains
The domain of an expression is the set of the possible values of that expression.
A domain is represented by a domain expression. For example:
"Integers"
"Booleans"
["FunctionOf", "Integers", "Integers"]
A domain expression is either a domain literal represented by an identifier
such as "Integers"
and "Booleans"
or a constructed domain represented by
a function expression.
Of course, it wouldn’t make sense for it to be any function, so the name of that function must be among a limited set of domain constructors.
This effectively defines a specialized language to represent domains. In some cases the same function name can be used in a domain expression and a value expression, but they will be interpreted differently.
Domains are similar to types in programming languages. Amongst other things, they are used to select the correct function definition.
For example a function Add
could have several implementation to operate on
numbers or on matrixes. The domain of the arguments would be used to select the
appropriate function definition.
Symbolic manipulation algorithms also use domains to decide when certain transformations are applicable.
For example, \( \sqrt{x^2} = x\) only if \(x \geq 0\)
Obtaining the Domain of an Expression
To query the domain of an expression read the domain
property of the
expression.
ce.box("Pi").domain;
// ➔ "TranscendentalNumbers"
ce.box("Divide").domain;
// ➔ '["FunctionOf", "Numbers", "Numbers", "Numbers]':
// domain of the function "Divide"
ce.box(["Add", 5, 2]).domain;
// ➔ "Numbers": the result of the "Add" function
// (its codomain) belongs to the domain "Numbers"
ce.box(["Add", 5, 2]).evaluate().domain;
// ➔ "Integers": once evaluated, the domain of
// the result may be more specific
Domain Lattice
Domains are defined in a hierarchy (a lattice). The upper bound of the
domain lattice is the Anything
domain (the top domain) and its lower bound is
the Void
domain (the bottom domain).
- The
Anything
domain contains all possible values and all possible domains. It is used when not much is known about the possible value of an expression. In some languages, this is called the universal type. - The
Void
domain contains no value. It is the subdomain of all domains. Also called the zero or empty domain. It is rarely used, but it could indicate the return domain of a function that never returns. Not to be confused withNothing
, which is used when a function returns nothing.
There are a few other important domains:
- The
Domains
domain contains all the domain expressions. - The
Values
domain contains all the expressions which are not domains,
for example the number42
, the symbolalpha
, the expression["Add", "x", 1]
. - The
Nothing
domain has exactly one value, the symbolNothing
. It is used when an expression has no other meaningful value. In some languages, this is called the unit type and the unit value. For example a function that returns nothing would have a return domain ofNothing
and would return theNothing
symbol.
The parent of a domain represents a is-a/subset-of relationship, for
example, a List
is-a Collections
.
The implementation of the CortexJS domains is based on Weibel, Trudy & Gonnet, Gaston. (1991). An Algebra of Properties… 352-359. 10.1145/120694.120749. .
Domain Compatibility
Two domains can be evaluated for their compatibility.
There are three kinds of compatibility that can be determined:
- Invariance: two domains are invariant if they represent exactly the same set of values
- Covariance: domain A is covariant with domain B if all the values
in A are also in B. For example
Integers
is covariant withNumbers
- Contravariant: domain A is contravariant with domain B if all the
values in B are in A. For example
Anything
is contravariant with every domain.
To evaluate the compatibility of two domains use domain.isCompatible()
By default, domain.isCompatible()
will check for covariant compatibility.
ce.domain("PositiveNumbers").isCompatible("Integers");
// ➔ true
ce.domain("Numbers").isCompatible("RealNumbers", "contravariant");
// ➔ true
Constructing New Domains
A domain constructor is a function expression with one of the identifiers below.
To define a new domain use a domain constructor.
// Functions with a single real number argument and that return an integer
["FunctionOf", "RealNumbers", "Integers"]
When a domain expression is boxed, it is automatically put in canonical form.
Domain Constructor | Description |
---|---|
FunctionOf |
["FunctionOf", ...<arg-domain>, <co-domain>] For example, ["FunctionOf", "Numbers", "Booleans"] is the domain of the functions that have a single argument, a number, and return a boolean (has a boolean codomain).By default, compatibility is determined by using covariance for the arguments and contravariance for the co-domain. |
ListOf |
["ListOf", <element-domain>] |
TupleOf |
["TupleOf", <element-1-domain>] , ["TupleOf", <element-1-domain>] ... <element-n-domain>] |
Intersection |
["Intersection", <domain-1>, <domain-2>] All the values that are a member of <domain-1> and <domain-2> |
Union |
["Union", <domain-1>, <domain-2>] All the values that are a member of <domain-1> or <domain-2> |
OptArg |
["OptArg", <domain>] A value of <domain> or Nothing |
VarArg |
["VarArg", <domain>] As a function argument zero or more values of <domain> . |
Head |
|
Symbol |
|
Literal |
This constructor defines a domain with a single value, the value of its argument. ` | |
Covariant |
|
Contravariant |
|
Invariant |
["Invariant", <domain>] This constructor indicate that a domain is compatible with this domain only if they are invariants with regard to each other. |
Multiple |
["Multiple", <factor>, <domain>, <offset>] The set of numbers that satisfy <factor> * x + <offset> with x in domain . For example, the set of odd numbers is ["Multiple", 2, "Integers", 1] |