]>
Only recently have I begun to realize that the problem is not merely one of technical mastery or the competent application of the rules ... but that there is actually something else which is guiding these rules. It actually involves a different level of mastery. It's quite a different process to do it right; and every single act that you do can be done in that sense well or badly. But even assuming that you have got the technical part clear, the creation of this quality is a much more complicated process of the most utterly absorbing and fascinating dimensions. It is in fact a major creative or artistic act -- every single little thing you do -- ...
-- Christopher Alexander
(from Patterns of Software by Richard Gabriel)
In this chapter we look at the key notion of type and its generalization mode. We show that every Axiom object has a type that determines what you can do with the object. In particular, we explain how to use types to call specific functions from particular parts of the library and how types and modes can be used to create new objects from old. We also look at Record and Union types and the special type Any. Finally, we give you an idea of how Axiom manipulates types and modes internally to resolve ambiguities.
The Axiom world deals with many kinds of objects. There are mathematical objects such as numbers and polynomials, data structure objects such as lists and arrays, and graphics objects such as points and graphic images. Functions are objects too.
Axiom organizes objects using the notion of domain of computation, or simply domain. Each domain denotes a class of objects. The class of objects it denotes is usually given by the name of the domain: Integer for the integers, Float for floating-point numbers, and so on. The convention is that the first letter of a domain name is capitalized. Similarly, the domain Polynomial(Integer) denotes ``polynomials with integer coefficients.'' Also, Matrix(Float) denotes ``matrices with floating-point entries.''
Every basic Axiom object belongs to a unique domain. The integer belongs to the domain Integer and the polynomial belongs to the domain Polynomial(Integer). The domain of an object is also called its type. Thus we speak of ``the type Integer'' and ``the type Polynomial(Integer).''
After an Axiom computation, the type is displayed toward the right-hand side of the page (or screen).
Here we create a rational number but it looks like the last result. The type however tells you it is different. You cannot identify the type of an object by how Axiom displays the object.
When a computation produces a result of a simpler type, Axiom leaves the type unsimplified. Thus no information is lost.
This seldom matters since Axiom retracts the answer to the simpler type if it is necessary.
When you issue a positive number, the type PositiveInteger is printed. Surely, also has type Integer! The curious reader may now have two questions. First, is the type of an object not unique? Second, how is PositiveInteger related to Integer?
Any domain can be refined to a subdomain by a membership predicate. A predicate is a function that, when applied to an object of the domain, returns either true or false. For example, the domain Integer can be refined to the subdomain PositiveInteger, the set of integers such that , by giving the Axiom predicate . Similarly, Axiom can define subdomains such as ``the subdomain of diagonal matrices,'' ``the subdomain of lists of length two,'' ``the subdomain of monic irreducible polynomials in ,'' and so on. Trivially, any domain is a subdomain of itself.
While an object belongs to a unique domain, it can belong to any number of subdomains. Any subdomain of the domain of an object can be used as the type of that object. The type of is indeed both Integer and PositiveInteger as well as any other subdomain of integer whose predicate is satisfied, such as ``the prime integers,'' ``the odd positive integers between 3 and 17,'' and so on.
In Axiom, domains are objects. You can create them, pass them to functions, and, as we'll see later, test them for certain properties.
In Axiom, you ask for a value of a function by applying its name to a set of arguments.
To ask for ``the factorial of '' you enter this expression to Axiom. This applies the function factorial to the value to compute the result.
Enter the type Polynomial (Integer) as an expression to Axiom. This looks much like a function call as well. It is! The result is appropriately stated to be of type Domain, which according to our usual convention, denotes the class of all domains.
The most basic operation involving domains is that of building a new domain from a given one. To create the domain of ``polynomials over the integers,'' Axiom applies the function Polynomial to the domain Integer. A function like Polynomial is called a domain constructor or, constructor:domain more simply, a constructor. A domain constructor is a function that creates a domain. An argument to a domain constructor can be another domain or, in general, an arbitrary kind of object. Polynomial takes a single domain argument while SquareMatrix takes a positive integer as an argument to give its dimension and a domain argument to give the type of its components.
What kinds of domains can you use as the argument to Polynomial or SquareMatrix or List? Well, the first two are mathematical in nature. You want to be able to perform algebraic operations like ``+'' and ``*'' on polynomials and square matrices, and operations such as determinant on square matrices. So you want to allow polynomials of integers and polynomials of square matrices with complex number coefficients and, in general, anything that ``makes sense.'' At the same time, you don't want Axiom to be able to build nonsense domains such as ``polynomials of strings!''
In contrast to algebraic structures, data structures can hold any kind of object. Operations on lists such as insertinsertList, deletedeleteList, and concatconcatList just manipulate the list itself without changing or operating on its elements. Thus you can build List over almost any datatype, including itself.
Create a complicated algebraic domain.
Try to create a meaningless domain.
Evidently from our last example, Axiom has some mechanism that tells what a constructor can use as an argument. This brings us to the notion of category. As domains are objects, they too have a domain. The domain of a domain is a category. A category is simply a type whose members are domains.
A common algebraic category is Ring, the class of all domains that are ``rings.'' A ring is an algebraic structure with constants and and operations +, -, and *. These operations are assumed ``closed'' with respect to the domain, meaning that they take two objects of the domain and produce a result object also in the domain. The operations are understood to satisfy certain ``axioms,'' certain mathematical principles providing the algebraic foundation for rings. For example, the additive inverse axiom for rings states:
Every element has an additive inverse such that .
The prototypical example of a domain that is a ring is the integers. Keep them in mind whenever we mention Ring.
Many algebraic domain constructors such as Complex, Polynomial, Fraction, take rings as arguments and return rings as values. You can use the infix operator ``'' to ask a domain if it belongs to a particular category.
All numerical types are rings. Domain constructor Polynomial builds ``the ring of polynomials over any other ring.''
Constructor List never produces a ring.
The constructor Matrix(R) builds ``the domain of all matrices over the ring .'' This domain is never a ring since the operations ``+'', ``-'', and ``*'' on matrices of arbitrary shapes are undefined.
Thus you can never build polynomials over matrices.
Use SquareMatrix(n,R) instead. For any positive integer , it builds ``the ring of by matrices over .''
Another common category is Field, the class of all fields. field A field is a ring with additional operations. For example, a field has commutative multiplication and a closed operation / for the division of two elements. Integer is not a field since, for example, does not have an integer result. The prototypical example of a field is the rational numbers, that is, the domain Fraction(Integer). In general, the constructor Fraction takes an IntegralDomain, which is a ring with additional properties, as an argument and returns a field. Actually, the argument domain must have some additional so as to belong to the category IntegralDomain Other domain constructors, such as Complex, build fields only if their argument domain is a field.
The complex integers (often called the ``Gaussian integers'') do not form a field.
But fractions of complex integers do.
The algebraically equivalent domain of complex rational numbers is a field since domain constructor Complex produces a field whenever its argument is a field.
The most basic category is Type. Type It denotes the class of all domains and subdomains. Note carefully that Type does not denote the class of all types. The type of all categories is Category. The type of Type itself is undefined. Domain constructor List is able to build ``lists of elements from domain '' for arbitrary simply by requiring that belong to category Type.
Now, you may ask, what exactly is a category? category Like domains, categories can be defined in the Axiom language. A category is defined by three components:
This last component is a new idea. And it is key to the design of Axiom! Because categories can extend one another, they form hierarchies. Detailed charts showing the category hierarchies in Axiom are displayed in Appendix (TPDHERE). There you see that all categories are extensions of Type and that Field is an extension of Ring.
The operations supported by the domains of a category are called the exports of that category because these are the operations made available for system-wide use. The exports of a domain of a given category are not only the ones explicitly mentioned by the category. Since a category extends other categories, the operations of these other categories---and all categories these other categories extend---are also exported by the domains.
For example, polynomial domains belong to PolynomialCategory. This category explicitly mentions some twenty-nine operations on polynomials, but it extends eleven other categories (including Ring). As a result, the current system has over one hundred operations on polynomials.
If a domain belongs to a category that extends, say, Ring, it is convenient to say that the domain exports Ring. The name of the category thus provides a convenient shorthand for the list of operations exported by the category. Rather than listing operations such as + and * of Ring each time they are needed, the definition of a type simply asserts that it exports category Ring.
The category name, however, is more than a shorthand. The name Ring, in fact, implies that the operations exported by rings are required to satisfy a set of ``axioms'' associated with the name Ring. This subtle but important feature distinguishes Axiom from other abstract datatype designs.
Why is it not correct to assume that some type is a ring if it exports all of the operations of Ring? Here is why. Some languages such as APL APL denote the Boolean constants true and false by the integers and respectively, then use ``+'' and ``*'' to denote the logical operators or and and. But with these definitions Boolean is not a ring since the additive inverse axiom is violated. That is, there is no inverse element such that , or, in the usual terms: true or a = false. This alternative definition of Boolean can be easily and correctly implemented in Axiom, since Boolean simply does not assert that it is of category Ring. This prevents the system from building meaningless domains such as Polynomial(Boolean) and then wrongfully applying algorithms that presume that the ring axioms hold.
Enough on categories. To learn more about them, see Chapter ugCategories . We now return to our discussion of domains.
Domains export a set of operations to make them available for system-wide use. Integer, for example, exports the operations + and = given by the signatures +: (Integer,Integer)->Integer and =: (Integer,Integer)->Boolean, respectively. Each of these operations takes two Integer arguments. The + operation also returns an Integer but = returns a Boolean: true or false. The operations exported by a domain usually manipulate objects of the domain---but not always.
The operations of a domain may actually take as arguments, and return as values, objects from any domain. For example, Fraction (Integer) exports the operations /: (Integer,Integer)->Fraction(Integer) and characteristiccharacteristicFraction: ->NonNegativeInteger.
Suppose all operations of a domain take as arguments and return as values, only objects from other domains. package This kind of domain constructor:package is what Axiom calls a package.
A package does not designate a class of objects at all. Rather, a package is just a collection of operations. Actually the bulk of the Axiom library of algorithms consists of packages. The facilities for factorization; integration; solution of linear, polynomial, and differential equations; computation of limits; and so on, are all defined in packages. Domains needed by algorithms can be passed to a package as arguments or used by name if they are not ``variable.'' Packages are useful for defining operations that convert objects of one type to another, particularly when these types have different parameterizations. As an example, the package PolynomialFunction2(R,S) defines operations that convert polynomials over a domain to polynomials over . To convert an object from Polynomial(Integer) to Polynomial(Float), Axiom builds the package PolynomialFunctions2(Integer,Float) in order to create the required conversion function. (This happens ``behind the scenes'' for you: see ugTypesConvert for details on how to convert objects.)
Axiom categories, domains and packages and all their contained functions are written in the Axiom programming language and have been compiled into machine code. This is what comprises the Axiom library. We will show you how to use these domains and their functions and how to write your own functions.
We have already seen in the last section ugTypesBasic several examples of types. Most of these examples had either no arguments (for example, Integer) or one argument (for example, Polynomial (Integer)). In this section we give details about writing arbitrary types. We then define modes and discuss how to write them. We conclude the section with a discussion on constructor abbreviations.
When might you need to write a type or mode? You need to do so when you declare variables.
You need to do so when you declare functions (See Section ugTypesDeclare ),
You need to do so when you convert an object from one type to another (See Section ugTypesConvert ).
You need to do so when you give computation target type information (See Section ugTypesPkgCall ).
A constructor with no arguments can be written either type:using parentheses with or without parentheses:using with types trailing opening and closing parentheses ``()''.
Boolean() is the same as Boolean
Integer() is the same as Integer
String() is the same as String
Void() is the same as Void
It is customary to omit the parentheses.
A constructor with one argument can frequently be type:using parentheses written with no parentheses:using with types parentheses. Types nest from right to left so that Complex Fraction Polynomial Integer is the same as Complex (Fraction (Polynomial (Integer))). You need to use parentheses to force the application of a constructor to the correct argument, but you need not use any more than is necessary to remove ambiguities.
Here are some guidelines for using parentheses (they are possibly slightly more restrictive than they need to be).
If the argument is an expression like then you must enclose the argument in parentheses.
If the type is to be used with package calling then you must enclose the argument in parentheses.
Alternatively, you can write the type without parentheses then enclose the whole type expression with parentheses.
If you supply computation target type information (See Section ugTypesPkgCall ) then you should enclose the argument in parentheses.
If the type itself has parentheses around it and we are not in the case of the first example above, then the parentheses can usually be omitted.
If the type is used in a declaration and the argument is a single-word type, integer or symbol, then the parentheses can usually be omitted.
If a constructor type:using parentheses has more than parentheses:using with types one argument, you must use parentheses. Some examples are
UnivariatePolynomial(x, Float)
MultivariatePolynomial([z,w,r], Complex Float)
SquareMatrix(3, Integer)
FactoredFunctions2(Integer,Fraction Integer)
A mode is a type that possibly is a question mark (?) or
contains one in an argument position. For example, the following are
all modes.
?
Polynomial ?
Matrix Polynomial ?
SquareMatrix(3,?)
Integer
OneDimensionalArray(Float)
As is evident from these examples, a mode is a type with a part that is not specified (indicated by a question mark). Only one ``?'' is allowed per mode and it must appear in the most deeply nested argument that is a type. Thus ?(Integer), Matrix(? (Polynomial)), SquareMatrix(?, Integer) (it requires a numeric argument) and SquareMatrix(?, ?) are all invalid. The question mark must take the place of a domain, not data. This rules out, for example, the two SquareMatrix expressions.
Modes can be used for declarations (See Section ugTypesDeclare ) and conversions (Section ugTypesConvert ). However, you cannot use a mode for package calling or giving target type information.
Every constructor has an abbreviation that abbreviation:constructor you can freely constructor:abbreviation substitute for the constructor name. In some cases, the abbreviation is nothing more than the capitalized version of the constructor name.
Aside from allowing types to be written more concisely, abbreviations are used by Axiom to name various system files for constructors (such as library filenames, test input files and example files). Here are some common abbreviations.
COMPLEX abbreviates Complex | DFLOAT abbreviates DoubleFloat |
EXPR abbreviates Expression | FLOAT abbreviates Float |
FRAC abbreviates Fraction | INT abbreviates Integer |
MATRIX abbreviates Matrix | NNI abbreviates NonNegativeInteger |
PI abbreviates PositiveInteger | POLY abbreviates Polynomial |
STRING abbreviates String | UP abbreviates UnivariatePolynomial |
You can combine both full constructor names and abbreviations in a type expression. Here are some types using abbreviations.
POLY INT | is the same as | Polynomial(INT) |
POLY(Integer) | is the same as | Polynomial(Integer) |
POLY(Integer) | is the same as | Polynomial(INT) |
FRAC(COMPLEX(INT)) | is the same as | Fraction Complex Integer |
FRAC(COMPLEX(INT)) | is the same as | FRAC(Complex Integer) |
There are several ways of finding the names of constructors and their abbreviations. For a specific constructor, use )abbreviation query. abbreviation You can also use the )what system command to see the names and abbreviations of constructors. what For more information about )what, see ugSysCmdwhat .
)abbreviation query can be abbreviated (no pun intended) to )abb q.
The )abbreviation query command lists the constructor name if you give the abbreviation. Issue )abb q if you want to see the names and abbreviations of all Axiom constructors.
Issue this to see all packages whose names contain the string ``ode''. what packages
A declaration is an expression used to restrict the type of values that can be assigned to variables. A colon ``:'' is always used after a variable or list of variables to be declared.
For a single variable, the syntax for declaration is
variableName typeOrMode
For multiple variables, the syntax is
(, , ... ): typeOrMode
You can always combine a declaration with an assignment. When you do, it is equivalent to first giving a declaration statement, then giving an assignment. For more information on assignment, see Section ugIntroAssign and Section ugLangAssign . To see how to declare your own functions, see ugUserDeclare .
This declares one variable to have a type.
This declares several variables to have a type.
, and can only hold integer values.
If a value cannot be converted to a declared type, an error message is displayed.
This declares a variable with a mode.
This declares several variables with a mode.
This complex object has integer real and imaginary parts.
This complex object has fractional symbolic real and imaginary parts.
This matrix has entries that are polynomials with integer coefficients.
This matrix has a single entry that is a polynomial with rational number coefficients.
This matrix has entries that are polynomials with complex integer coefficients.
Note the difference between this and the next example. This is a complex object with polynomial real and imaginary parts.
This is a polynomial with complex integer coefficients. The objects are convertible from one to the other. See ugTypesConvert for more information.
A Record is an object composed of one or more other objects, Record each of which is referenced selector:record with record:selector a selector. Components can all belong to the same type or each can have a different type.
Record components are implicitly ordered. All the components of a record can be set at once by assigning the record a bracketed tuple of values of the proper length. For example:
To access a component of a record , write the name , followed by a period, followed by a selector.
The object returned by this computation is a record with two components: a part and a part.
This is the quotient part.
This is the remainder part.
You can use selector expressions on the left-hand side of an assignment to change destructively the components of a record.
The selected component has the value , which is what is returned by the assignment. Check that the value of was modified.
Selectors are evaluated. Thus you can use variables that evaluate to selectors instead of the selectors themselves.
Be careful! A selector could have the same name as a variable in the workspace. If this occurs, precede the selector name by a single quote, as in selector:quoting u.'quotient.
Here we declare that the value of has two components: a string, to be accessed via name, and an integer, to be accessed via birthdayMonth.
You must initially set the value of the entire Record at once.
Once set, you can change any of the individual components.
Records may be nested and the selector names can be shared at different levels.
The record has a selector at two different levels. Here is an initial value for .
This extracts the component from the component of .
This extracts the component from .
You can also use spaces or parentheses to refer to Record components. This is the same as .
This is the same as .
This is the same as .
Look at to make sure it was modified.
Type Union is used for objects that can be of any of a specific finite set of types. Union Two versions of unions are available, one with selectors (like records) and one without. union
The declaration states that can have values that are integers, strings or ``big'' floats. If, for example, the Union object is an integer, the object is said to belong to the Integer branch of the Union. Note that we are being a bit careless with the language here. Technically, the type of is always Union(Integer, String, Float). If it belongs to the Integer branch, may be converted to an object of type Integer.
The syntax for writing a Union type without selectors is
Union(, , ..., )
The types in a union without selectors must be distinct.
It is possible to create unions like Union(Integer, PositiveInteger) but they are difficult to work with because of the overlap in the branch types. See below for the rules Axiom uses for converting something into a union object.
The case infix case operator returns a Boolean and can be used to determine the branch in which an object lies.
This function displays a message stating in which branch of the Union the object (defined as above) lies.
This tries sayBranch with an integer.
This tries sayBranch with a string.
This tries sayBranch with a floating-point number.
There are two things of interest about this particular example to which we would like to draw your attention.
Sometimes Union types can have extremely long names. Axiom therefore abbreviates the names of unions by printing the type of the branch first within the Union and then eliding the remaining types with an ellipsis (...).
Here the Integer branch is displayed first. Use ``::'' to create a Union object from an object.
Here the String branch is displayed first.
Use typeOf to see the full and actual Union type. typeOf
A common operation that returns a union is exquoexquoInteger which returns the ``exact quotient'' if the quotient is exact,
and "failed" if the quotient is not exact.
A union with a "failed" is frequently used to indicate the failure or lack of applicability of an object. As another example, assign an integer a variable declared to be a rational number.
The operation retractIfCanretractIfCanFraction tries to retract the fraction to the underlying domain Integer. It produces a union object. Here it succeeds.
Assign it a rational number.
Here the retraction fails.
Like records (ugTypesRecords ), you can write Union types selector:union with selectors. union:selector
The syntax for writing a Union type with selectors is
Union(:, :, ..., :)
You must be careful if a selector has the same name as a variable in
the workspace. If this occurs, precede the selector name by a single
quote quote. selector:quoting It is an error to use a
selector that does not correspond to the branch of the Union in
which the element actually lies.
Be sure to understand the difference between records and unions with selectors. union:difference from record Records can have more than one component and the selectors are used to refer to the components. record:difference from union Unions always have one component but the type of that one component can vary. An object of type Record(a: Integer, b: Float, c: String) contains an integer and a float and a string. An object of type Union(a: Integer, b: Float, c: String) contains an integer or a float or a string.
Here is a version of the sayBranch function (cf. ugTypesUnionsWOSel ) that works with a union with selectors. It displays a message stating in which branch of the Union the object lies.
Note that case uses the selector name as its right-hand argument. case If you accidentally use the branch type on the right-hand side of case, false will be returned.
Declare variable to have a union type with selectors.
Give an initial value to .
Use to determine in which branch of a Union an object lies.
To access the element in a particular branch, use the selector.
With the exception of objects of type Record, all Axiom data structures are homogenous, that is, they hold objects all of the same type. Any If you need to get around this, you can use type Any. Using Any, for example, you can create lists whose elements are integers, rational numbers, strings, and even other lists.
Declare to have type Any.
Assign a list of mixed type values to
When we ask for the elements, Axiom displays these types.
Actually, these objects belong to Any but Axiom automatically converts them to their natural types for you.
Since type Any can be anything, it can only belong to type Type. Therefore it cannot be used in algebraic domains.
Perhaps you are wondering how Axiom internally represents objects of type Any. An object of type Any consists not only of a data part representing its normal value, but also a type part (a badge) giving badge its type. For example, the value of type PositiveInteger as an object of type Any internally looks like .
When should you use Any instead of a Union type? For a Union, you must know in advance exactly which types you are going to allow. For Any, anything that comes along can be accommodated.
Conversion is the process of changing an object of one type into an object of another type. The syntax for conversion is:
By default, has the type PositiveInteger.
We can change this into an object of type Fraction Integer by using ``::''.
A coercion is a special kind of conversion that Axiom is allowed to do automatically when you enter an expression. Coercions are usually somewhat safer than more general conversions. The Axiom library contains operations called coerce and convert. Only the coerce operations can be used by the interpreter to change an object into an object of another type unless you explicitly use a ::.
By now you will be quite familiar with what types and modes look like. It is useful to think of a type or mode as a pattern for what you want the result to be.
Let's start with a square matrix of polynomials with complex rational number coefficients. SquareMatrix
We first want to interchange the Complex and Fraction layers. We do the conversion by doing the interchange in the type expression.
Interchange the Polynomial and the Fraction levels.
Interchange the Polynomial and the Complex levels.
All the entries have changed types, although in comparing the last two results only the entry in the lower left corner looks different. We did all the intermediate steps to show you what Axiom can do.
In fact, we could have combined all these into one conversion.
There are times when Axiom is not be able to do the conversion in one step. You may need to break up the transformation into several conversions in order to get an object of the desired type.
We cannot move either Fraction or Complex above (or to the left of, depending on how you look at it) SquareMatrix because each of these levels requires that its argument type have commutative multiplication, whereas SquareMatrix does not. That is because Fraction requires that its argument belong to the category IntegralDomain and category Complex requires that its argument belong to CommutativeRing. See ugTypesBasic for a brief discussion of categories. The Integer level did not move anywhere because it does not allow any arguments. We also did not move the SquareMatrix part anywhere, but we could have.
Recall that looks like this.
If we want a polynomial with matrix coefficients rather than a matrix with polynomial entries, we can just do the conversion.
We have not yet used modes for any conversions. Modes are a great shorthand for indicating the type of the object you want. Instead of using the long type expression in the last example, we could have simply said this.
We can also indicate more structure if we want the entries of the matrices to be fractions.
A subdomain S of a domain D is a domain consisting of
Every domain is a subdomain of itself, trivially satisfying the membership test: true.
Currently, there are only two system-defined subdomains in Axiom that receive substantial use. PositiveInteger and NonNegativeInteger are subdomains of Integer. An element of NonNegativeInteger is an integer that is greater than or equal to zero, that is, satisfies . An element of PositiveInteger is a nonnegative integer that is, in fact, greater than zero, that is, satisfies . Not all operations from Integer are available for these subdomains. For example, negation and subtraction are not provided since the subdomains are not closed under those operations. When you use an integer in an expression, Axiom assigns to it the type that is the most specific subdomain whose predicate is satisfied.
This is a positive integer.
This is a nonnegative integer.
This is neither of the above.
Furthermore, unless you are assigning an integer to a declared variable or using a conversion, any integer result has as type the most specific subdomain.
When necessary, Axiom converts an integer object into one belonging to a less specific subdomain. For example, in , the arguments to "-" are both elements of PositiveInteger, but this type does not provide a subtraction operation. Neither does NonNegativeInteger, so and are viewed as elements of Integer, where their difference can be calculated. The result is , which Axiom then automatically assigns the type PositiveInteger.
Certain operations are very sensitive to the subdomains to which their arguments belong. This is an element of PositiveInteger.
This is an element of Fraction Integer.
It makes sense then that this is a list of elements of PositiveInteger.
What should the type of [10**(i-1) for i in 2..5] be? On one hand, is always an integer greater than zero as ranges from to and so is also always a positive integer. On the other, is a very simple function of . Axiom does not try to analyze every such function over the index's range of values to determine whether it is always positive or nowhere negative. For an arbitrary Axiom function, this analysis is not possible.
So, to be consistent no such analysis is done and we get this.
To get a list of elements of PositiveInteger instead, you have two choices. You can use a conversion.
Or you can use pretend. pretend
The operation pretend is used to defeat the Axiom type system. The expression object pretend D means ``make a new object (without copying) of type D from object.'' If object were an integer and you told Axiom to pretend it was a list, you would probably see a message about a fatal error being caught and memory possibly being damaged. Lists do not have the same internal representation as integers!
You use pretend at your peril. peril
Use with great care! Axiom trusts you that the value is of the specified type.
Axiom works hard to figure out what you mean by an expression without your having to qualify it with type information. Nevertheless, there are times when you need to help it along by providing hints (or even orders!) to get Axiom to do what you want.
We saw in ugTypesDeclare that declarations using types and modes control the type of the results produced. For example, we can either produce a complex object with polynomial real and imaginary parts or a polynomial with complex integer coefficients, depending on the declaration.
Package calling is how you tell Axiom to use a particular function from a particular part of the library.
Use the / from Fraction Integer to create a fraction of two integers.
If we wanted a floating point number, we can say ``use the / in Float.''
Perhaps we actually wanted a fraction of complex integers.
In each case, AXIOM used the indicated operations, sometimes first needing to convert the two integers into objects of the appropriate type. In these examples, ``/'' is written as an infix operator.
To use package calling with an infix operator, use the following syntax:
We used, for example, . The expression is equivalent to . Therefore in the expression the second ``+'' comes from the Float domain. The first ``+'' comes from Float because the package call causes AXIOM to convert and to type Float. Before the sum is converted, it is given a target type of Float by AXIOM and then evaluated. The target type causes the ``+'' from Float to be used.
For an operator written before its arguments, you must use parentheses around the arguments (even if there is only one), and follow the closing parenthesis by a `` $'' and then the type.
For example, to call the ``minimum'' function from SmallFloat on two integers, you could write min(4,89) $SmallFloat. Another use of package calling is to tell AXIOM to use a library function rather than a function you defined. We discuss this in Section ugUserUse .
Sometimes rather than specifying where an operation comes from, you just want to say what type the result should be. We say that you provide a target type for the expression. Instead of using a `` $'', use a ``@'' to specify the requested target type. Otherwise, the syntax is the same. Note that giving a target type is not the same as explicitly doing a conversion. The first says ``try to pick operations so that the result has such-and-such a type.'' The second says ``compute the result and then convert to an object of such-and-such a type.''
Sometimes it makes sense, as in this expression, to say ``choose the operations in this expression so that the final result is Float.
Here we used ``@'' to say that the target type of the left-hand side was Float. In this simple case, there was no real difference between using `` $'' and ``@''. You can see the difference if you try the following.
This says to try to choose ``+'' so that the result is a string. Axiom cannot do this.
This says to get the + from String and apply it to the two integers. Axiom also cannot do this because there is no + exported by String.
(By the way, the operation concatconcatString or juxtaposition is used to concatenate two strings.) String
When we have more than one operation in an expression, the difference is even more evident. The following two expressions show that Axiom uses the target type to create different objects. The ``+'', ``*'' and ``**'' operations are all chosen so that an object of the correct final type is created.
This says that the operations should be chosen so that the result is a Complex object.
This says that the operations should be chosen so that the result is a Polynomial object.
What do you think might happen if we left off all target type and package call information in this last example?
We can convert it to Complex as an afterthought. But this is more work than just saying making what we want in the first place.
Finally, another use of package calling is to qualify fully an operation that is passed as an argument to a function.
Start with a small matrix of integers.
We want to produce a new matrix that has for entries the multiplicative inverses of the entries of . One way to do this is by calling mapmapMatrixCategoryFunctions2 with the invinvFraction function from Fraction (Integer).
We could have been a bit less verbose and used abbreviations.
As it turns out, Axiom is smart enough to know what we mean anyway. We can just say this.
In this section we briefly describe an internal process by which resolve Axiom determines a type to which two objects of possibly different types can be converted. We do this to give you further insight into how Axiom takes your input, analyzes it, and produces a result.
What happens when you enter to Axiom? Let's look at what you get from the two terms of this expression.
This is a symbolic object whose type indicates the name.
This is a positive integer.
There are no operations in PositiveInteger that add positive integers to objects of type Variable(x) nor are there any in Variable(x). Before it can add the two parts, Axiom must come up with a common type to which both and can be converted. We say that Axiom must resolve the two types into a common type. In this example, the common type is Polynomial(Integer).
Once this is determined, both parts are converted into polynomials, and the addition operation from Polynomial(Integer) is used to get the answer.
Axiom can always resolve two types: if nothing resembling the original types can be found, then Any is be used. Any This is fine and useful in some cases.
In other cases objects of type Any can't be used by the operations you specified.
Although this example was contrived, your expressions may need to be qualified slightly to help Axiom resolve the types involved. You may need to declare a few variables, do some package calling, provide some target type information or do some explicit conversions.
We suggest that you just enter the expression you want evaluated and see what Axiom does. We think you will be impressed with its ability to ``do what I mean.'' If Axiom is still being obtuse, give it some hints. As you work with Axiom, you will learn where it needs a little help to analyze quickly and perform your computations.
In this section we discuss how Axiom makes some operations available to you while hiding others that are meant to be used by developers or only in rare cases. If you are a new user of Axiom, it is likely that everything you need is available by default and you may want to skip over this section on first reading.
Every constructor:exposed domain and package in the Axiom library constructor:hidden is exposed:constructor either exposed (meaning that you can use its operations without doing anything special) or it is hidden (meaning you have to either package call (see ugTypesPkgCall) the operations it contains or explicitly expose it to use the operations). The initial exposure status for a constructor is set in the file exposed.lsp (see the Installer's Note exposed.lsp @ exposed.lsp for Axiom file:exposed.lsp @ exposed.lsp if you need to know the location of this file). Constructors are collected together in group:exposure exposure groups. exposure:group Categories are all in the exposure group ``categories'' and the bulk of the basic set of packages and domains that are exposed are in the exposure group ``basic.'' Here is an abbreviated sample of the file (without the Lisp parentheses):
For each constructor in a group, the full name and the abbreviation is given. There are other groups in exposed.lsp but initially only the constructors in exposure groups ``basic'' ``categories'' ``naglink'' and ``anna'' are exposed.
As an interactive user of Axiom, you do not need to modify this file. Instead, use )set expose to expose, hide or query the exposure status of an individual constructor or exposure group. set expose The reason for having exposure groups is to be able to expose or hide multiple constructors with a single command. For example, you might group together into exposure group ``quantum'' a number of domains and packages useful for quantum mechanical computations. These probably should not be available to every user, but you want an easy way to make the whole collection visible to Axiom when it is looking for operations to apply.
If you wanted to hide all the basic constructors available by default, you would issue )set expose drop group basic. set expose drop group We do not recommend that you do this. If, however, you discover that you have hidden all the basic constructors, you should issue )set expose add group basic to restore your default environment. set expose add group
It is more likely that you would want to expose or hide individual constructors. In ugUserTriangle we use several operations from OutputForm, a domain usually hidden. To avoid package calling every operation from OutputForm, we expose the domain and let Axiom conclude that those operations should be used. Use )set expose add constructor and )set expose drop constructor to expose and hide a constructor, respectively. set expose drop constructor You should use the constructor name, not the abbreviation. The )set expose command guides you through these options. set expose add constructor
If you expose a previously hidden constructor, Axiom exhibits new behavior (that was your intention) though you might not expect the results that you get. OutputForm is, in fact, one of the worst offenders in this regard. OutputForm This domain is meant to be used by other domains for creating a structure that Axiom knows how to display. It has functions like + that form output representations rather than do mathematical calculations. Because of the order in which Axiom looks at constructors when it is deciding what operation to apply, OutputForm might be used instead of what you expect.
This is a polynomial.
Expose OutputForm.
This is what we get when OutputForm is automatically available.
Hide OutputForm so we don't run into problems with any later examples!
Finally, exposure is done on a frame-by-frame basis. A frame (see ugSysCmdframe ) frame:exposure and is one of possibly several logical Axiom workspaces within a physical one, each having its own environment (for example, variables and function definitions). If you have several Axiom workspace windows on your screen, they are all different frames, automatically created for you by HyperDoc. Frames can be manually created, made active and destroyed by the )frame system command. frame They do not share exposure information, so you need to use )set expose in each one to add or drop constructors from view.
To conclude this chapter, we introduce you to some system commands that you can use for getting more information about domains, packages, categories, and operations. The most powerful Axiom facility for getting information about constructors and operations is the Browse component of HyperDoc. This is discussed in Chapter ugBrowse .
Use the )what system command to see lists of system objects whose name contain a particular substring (uppercase or lowercase is not significant). what
Issue this to see a list of all operations with ``complex'' in their names. what operation
If you want to see all domains with ``matrix'' in their names, issue this. what domain
Similarly, if you wish to see all packages whose names contain ``gauss'', enter this. what packages
This command shows all the operations that Any provides. Wherever $ appears, it means ``Any''. show
This displays all operations with the name complex. display operation
Let's analyze this output.
First we find out what some of the abbreviations mean.
So if D1 is a commutative ring (such as the integers or floats) and D belongs to ComplexCategory D1, then there is an operation called complex that takes two elements of D1 and creates an element of D. The primary example of a constructor implementing domains belonging to ComplexCategory is Complex. See Complex for more information on that and see ugUserDeclare for more information on function types.