This text has been extracted from the original WINNIAL.HLP file,
converted from RTF to HTML using LibreOffice, and then liberally edited with Sublime Text.
It has not been extensively proofread, so patches are welcome.
As a reference, you should find the RTF file extracted from the original HLP file
in the same source directory containing this file.
The operation
abs
produces the following results when applied to atoms of the six types:
Atomic Type
Result
boolean
corresponding integer
integer
absolute value
real
absolute value
character
fault ?A
phrase
fault ?A
fault
argument A
If the argument is the largest negative integer for the computer, the result is the corresponding positive real number because the corresponding positive integer is too large for representation as an integer.
abs l -2 3.5 `a "abc ??error
1 2 3.5 ?A ?A ?error
The operation
abs
can be used to convert a boolean value to an integer. Its primary use is in the numeric domains, where it is used to give a positive number measuring the size of a number.
The transformer
accumulate
is similar to the concept of scan in APL. It computes the partial reductions of the initial lists of the items of
A, using
reduce f
(a right-to-left reduction) to compute each partial result. (A reductive operation is one which when applied to an array having a number of items returns a single result.)
In the example below, the
initial lists
of
Z
are:
[13, 13 39, 13 39 92, 13 39 92 45];
and the right-to-left reduction of
f
on
Z
is equivalent to:
[f 13, 13 f 39, 13 f (39 f 92), 13 f (39 f ( 92 f 45))]
Z := 13 39 92 45 ;
ACCUMULATE pass Z
+--+-----+----------+---------------+
|13|13 39|+--+-----+|+--+----------+|
| | ||13|39 92|||13|+--+-----+||
| | |+--+-----+|| ||39|92 45|||
| | | || |+--+-----+||
| | | |+--+----------+|
+--+-----+----------+---------------+
The first example shows that accumulate builds the result values with a right-to-left reduction.
ACCUMULATE max Z
13 39 92 92
ACCUMULATE min Z
13 13 13 13
ACCUMULATE sum (count 10)
1 3 6 10 15 21 28 36 45 55
ACCUMULATE minus (count 10)
1 -1 2 -2 3 -3 4 -4 5 -5
Useful applications of
accumulate
include the
accumulate sum
transform used to compute a running sum, and the
accumulate minus
transform which gives an alternating sum.
ACCUMULATE or oollooll
oollllll
S := ' black and white'
black and white
B := not (S match ` )
oolllllolllolllll
ACCUMULATE or B sublist S
black and white
The
accumulate or
transform produces a bitstring that can be used with
sublist
to drop leading items that fail to meet a predicate.
 It has three operation arguments:
endf
is applied to the end argument before starting to build the result,
parta
computes the left value from the argument, which is stacked, and
joinf
combines the left and right values as the recursion unwinds.
ACROSS [ 0 first, pass, plus ] 3 4 5 6
18
reshape [ shape, ACROSS [pass, pass, CONVERSE append ] ] 2 15 12 4
4 12 15 2
Definition
ACROSS IS TRANSFORMER endf parta joinf OPERATION A {
A := list A;
Res := endf void last A;
FOR I WITH reverse tell tally A DO
Res := parta A@I joinf Res
ENDFOR }
Equations
ACROSS [endf, parta, joinf] A = RECUR [empty, endf, parta first, joinf, rest ] list A
sum A = ACROSS [ 0 first, pass, plus ] A
reverse A = reshape [ shape, ACROSS [pass, pass, CONVERSE append ] ] A
EACH f A = reshape [ shape, ACROSS [ Null first, f, hitch ] ] A
An
action
is the construct that is entered in the interactive loop of the Q’Nial interpreter or accepted as an input unit within the operation loaddefs:
If an action is a definition-sequence, its definitions are installed in the global environment.
If an action is an expression-sequence, it is executed and a value is returned. The value returned by an expression-sequence is displayed on the screen unless it is the fault ?noexpr.
An external-declaration assigns a role to a name in the global environment so that the name can be used in other definitions before it is completely specified.
A remark is an input to the Q’Nial interpreter that is not processed. It begins with a line that has the symbol # as the first non-blank character in the line. In direct input at the top level loop, a remark ends at the end of the line unless a backslash symbol ( \ ) is used to extend the line. In a definition file, a remark ends at the first blank line. A remark cannot appear within a definition or expression-sequence.
An
address
is an integer or a list of integers describing the location of an item in an array. An address uses 0-origin counting, i.e. the first position in a list is at address 0. All the addresses of an array can be stored in an array of the same rectangular structure as the array itself. Such an array is called the
grid
of an array. Consider the following example:
The array on the right is a 4 by 6 table of numbers. It has 24 items arranged along two directions of length 4 and 6 respectively. The address of 496 is [2,3].
The addresses of a
list
are integers: 0, 1, etc. For convenience, in situations where an address of a list is expected, a solitary integer is treated as an integer.
The operation
allbools
tests whether or not
A
is a nonempty array of boolean items. It returns
true
if
A
is not empty and all the items of
A
are boolean atoms,
false
otherwise.
allbools lool
l
allbools 1 0 1
o
allbools Null
o
Definition
allbools IS OPERATION A { and EACH isboolean A and not empty A }
The operation
allchars
tests whether or not
A
is a nonempty array of character items. It returns
true
if
A
is not empty and all the items of
A
are character atoms,
false
otherwise.
allchars 'apple pie'
l
allchars "today
o
allchars Null
o
Definition
allchars IS OPERATION A { and EACH ischar A and not empty A }
The operation
allin
tests whether or not all the items of array
A
are also items of array
B. The result is
true
if the test holds and
false
if it does not.
3 5 7 allin count 10
l
'ae' allin 'where are you?'
l
'where' allin 'hear'
o
The items of
A
and
B
are compared for exact equality, including type. The items being compared may be atoms or they may be any other array.
Definition
allin IS OPERATION A B { and ( A EACHLEFT in B ) }
The operation
allints
tests whether or not
A
is a nonempty array of integer items. It returns
true
if
A
is not empty and all the items of
A
are integer atoms,
false
otherwise.
allints lool
o
allints 1 0 25
l
allints Null
o
Definition
allints IS OPERATION A { and EACH isinteger A and not empty A }
The operation
allnumeric
tests whether or not
A
is a nonempty array with all atoms of the same numeric type.  It returns
true
if
A
is not empty and all the items of
A
are real atoms,
false
otherwise.
allreals 2 2.5 47.9
o
allreals 2.5 -7.8 27.3
l
allreals Null
o
Definition
allreals IS OPERATION A { and EACH isreal A and not empty A }
The operation
and
applied to a boolean array
A
does the boolean product of its items. If all items of
A
are
true, the result is
true; otherwise it is
false. In binary form and implements the and-connective of logic.
If
A
is a simple array and has a non-boolean item, the result is the logical fault
?L. The operation extends to non-simple arrays by the multi pervasive mechanism.
l and l
l
and llllloll
o
lloo and lolo
looo
And
is a reductive operation in that it reduces an array of booleans to a single boolean. If its argument is a pair of bitstrings or a list of bitstrings,
and
is applied to bits in corresponding positions of each item of the argument producing a bitstring of the same length as one of the items of its argument.
Equations
A and B = B and A
not and A =f= or EACH not A
and Null = True
The first example shows list (5 6 7) appended to list (2 3 4). The second example shows that if an array is appended to an empty list, the result is the solitary of the second argument. If
append
is applied to an array that is not a pair, a fault is returned.
Definition
append IS OPERATION A B { A link solitary B }
Equations
A append B = ( list A ) append B
Null append A = solitary A
A append B = list (A append B)
The operation
appendfile
writes a list of character arrays
A
to the end of the file named
Filename, using one line of the file for each row in the items of
A. If the file does not exist, it is created by the operation. The file must not be open.
Appendfile
is similar to operation
putfile
except that
putfile
overwrites an existing file.
Getfile
reads a file containing text records.
putfile "Mailing ['1','2','3'];
appendfile "Mailing ['Mr. Jones','123 Main St','New York NY 12345'];
getfile "Mailing
+-+-+-+---------+-----------+-----------------+
|1|2|3|Mr. Jones|123 Main St|New York NY 12345|
+-+-+-+---------+-----------+-----------------+
Definition
appendfile IS OPERATION Filename A {
Fnum := open Filename "a ;
ITERATE (Fnum writefile) (link EACH rows A) ;
close Fnum ; }
The operation
apply
carries out the application of the operation represented by the array
Op
to array
A.
Op
may be a phrase or string that names a predefined or user-defined operation or it may be the
cast
of an operation expression.
If the operation is represented by a phrase or string giving its name, the name is sought in the environment where
apply
is being used.
apply "second (3 4 5)
4
The operation to be applied may also be represented by the
parse tree
or
cast
for the operation expression. If the cast is a name, it is sought in the environment where the cast appears. In the following example,
!first
is the cast of the operation
first.
As seen in the second example, an operation expression does not have to be named in order to be used with
apply. The entire first example is enclosed in parentheses to avoid conflict with the notation for executing a host command.
An
argument
is an array value supplied to an operation. All operations in Nial take a single array as the argument, but binary operations expect that argument to be a pair. When an operation is used in infix syntax, then the two array values on the left and right are combined into a pair to be supplied as the argument to the operation.
The argument to an operation-form is treated specially. If the operation-form has only one formal parameter then the argument is assigned to the parameter. If it has two or more formal parameters, then the argument is checked to see if it has the same number of items as there are parameters. If so, the items are assigned to the parameters. If not, the result is the fault
?op_parameter.
The data objects of Nial are nested rectangular arrays. Atomic data objects such as numbers and characters are included within this description by virtue of an atom being considered as a self-containing array object with no dimensions.
Atomic Arrays
There are six types of atoms in Nial. They are
boolean, integer, real, character, phrase
and
fault. The first three are
numeric
types and are used for arithmetic operations. The last three are
literal
types and are used for text and symbol manipulation. All six types of atoms are used in comparisons.
Rectangularity Structure
An
array
is a collection of data objects having its
items
held at locations in a rectangular structure. The items are viewed as being at locations that are positioned relative to a set of directions at right angles to each other. The items may be arranged along zero, one, two or more directions. For example, the following array is a 4 by 6 table of numbers. It has 24 items arranged along two directions of length 4 and 6 respectively.
The operation
assign
assigns the value of
A
to the variable named by
Nm.
Nm
may be a phrase or string (e.g.
"X
or
'X'), in which case the named variable is sought in the current environment; or it may be the cast of a name (e.g.
!X), in which case it is sought in the environment in which it was cast. If the variable is found, its value is replaced by the array
A. Otherwise, a variable with that name is created in the global environment and given
A
as its value.
Any phrase may be used as a variable name with the operation
assign. Thus, it is possible to assign a value to names which are invalid identifiers. The associated value can be retrieved using the operation
value. The result of the operation is
A.
assign "X (2 3 4)
2 3 4
(!Var assign 2 3 4)
2 3 4
In the first example, the variable
X
is assigned the list 2 3 4. If this is done in an environment where
X
is a local variable, the local variable is updated. If
X
does not exist, it is created in the global environment.
In the second example, a variable
Var
must exist in the environment where the cast is done. The cast can be formed in a more global environment and passed into an operation as a parameter (This technique is called
by-variable
parameter passing).
An
assign-expression
assigns an array value to one or more variables at the time of evaluation of the assign-expression. The semantics of an assign-expression is interpreted in two stages: when the expression is analyzed (parsed) and when it is executed.
During the parse of the assign-expression appearing in a block, each name on the variable list is sought in the local environment. If the name exists in the local environment, the assignment affects the local association. If a name does not exist in the local environment and no reference has been made to a nonlocal variable with the same name, a local variable is created in the block. An assign-expression parsed in the global environment creates a global variable if a variable with that name does not already exist.
When an assign-expression is executed, the expression on the right of the assignment symbol ( := ) is evaluated. If the variable list on the left has only one name, the value of the expression is assigned to that variable. That is, the value is associated with that name.
If the variable list has several names, the items of the value are assigned to the variables in the order in which they appear. If the number of items does not match the number of variables, the fault ?assignment is returned as the value of the assign-expression. Otherwise, the value of the assign-expression is the value of the expression on the right.
When an indexed-variable is used on the left in an assign-expression, the parts of the array associated with the variable at the locations specified by the index are replaced by the values of the expression on the right.
If the index-expression for an indexed-variable assignment specifies a number of locations (at-all or slice indexing), there are two cases: if the value on the right is a single, the item of the single is placed in each location; otherwise, the value on the right must have the same number of items as the index expression indicates and the corresponding locations are updated with the items of the array value.
An
atlas
is an operation made up of a list of component operations. The result of applying an atlas is a list of the same length as the atlas. Each operation in the atlas is applied in turn to the argument resulting in an array value that becomes the item of the result list in the corresponding position. An atlas is used by the transformers
FORK, INNER
and
TEAM.
The operation
atomic
tests whether or not its argument is an atom. It returns
true
if it is and
false
if it is not.
atomic 3.5
l
atomic "hello
l
atomic 'hello'
o
The examples illustrate that a number and a phrase are atoms and that a string is not an atom.
An atom is a primitive concept in array theory and Nial. Atoms are distinguished from other arrays by the property that they are self nesting. The definition of atomic is based on this property.
 Many of the built-in operations of Nial are binary. They can be used in both an infix and a prefix manner. If a binary operation
f
is used in infix syntax then the arguments on each side of
f
are treated as the two items of its argument.
3 reshape 5
In prefix usage,
f
can precede a single array with two items, or an explicit pair formed with strand notation or bracket-comma notation.
X := 3 5;
reshape X
5 5 5
reshape 3 5
5 5 5
reshape [3,5]
5 5 5
Each operation
f
in this class maps a pair of atoms to an atom.
A binary pervasive operation maps two arrays having identical structure to one with the same structure, mapping each pair of corresponding atoms by the function's behaviour on pairs of atoms.
All of the binary operations of arithmetic and logic are binary pervasive.
If a binary pervasive operation is applied to a pair of arrays that do not have the same shape, the effect is to build a conformable pair by replicating an atom or solitary item of the pair to the shape of the other item. If both items are of unequal shape and if both items are made up of more than one item, the fault
?conform
is returned. The replication of an argument with one item provides binary pervasive operations with a scalar extension capability. For example,
3 4 5 6 - 5 = (3 4 5 6 - 5 5 5 5)
l
If a binary pervasive operation is applied to an array that is not a pair, a fault is returned.
The following table lists the binary pervasive operations.
Operation
Function
divide
division of numbers
gt
greater than comparison
gte
greater than or equal comparison
lt
less than comparison
lte
less than or equal comparison
match
equality of atoms without type coercion
mate
equality of atoms with type coercion
minus
subtraction of numbers
mod
remainder of division of integers
plus
addition of numbers
quotient
quotient on division of integers
times
multiplication of numbers divide division of numbers
Equations
A f B = A EACHBOTH f B
A f B = EACH f (A pack B)
shape (A f B) = shape pack A B
The operation
blend
combines the top two levels of an array
B
into a single level blending the axes of the second level of
B
into the combined level according to the axis numbers given in
A. The items of
B
must be of the same shape.
If the array
B
is equivalent to
A split C
for some array
C, the result of
A blend B
is
C.
The tally of
A
is the valence of an item of
B. The items of
A
indicate where the axes of the items of
B
are placed in the shape of the result.
In the example,
B
is a list of four tables of shape 3 2 created by a
2 0 split. The result of
2 0 blend B
is the array of valence 3 that is the second argument to
split.
Equations
SORT <= list I = axes A and not empty A ==> I blend (I split A) = A
equal EACH shape A and not empty A ==> (valence A+axes first A) blend A = mix A
A blend B =f= [Null,A] PARTITION first B
A
block
is a scope-creating mechanism that permits an expression-sequence to be created so that it has local definitions and variables which are visible only inside the block. A block may appear as a primary-expression or as the body of an operation-form.
A local environment is a collection of associations that are known within a limited section of program text. These limited sections are formed by blocks, operation-forms and transformer-forms. A name that has a local association in one of these forms is said to have local scope.
If the definition appears within a block, the association is made in the local environment. Otherwise, the association is made in the global environment and assigns a role to the name as representing that kind of expression.
If a block is used as a primary-expression, the local environment created by a block is determined by the block itself. If it is the body of an operation-form, the local environment includes the formal parameter names of the operation-form as variables.
Local and Nonlocal Declaration
The identifiers included in the local and nonlocal declarations are declared to be variables. Both forms of declarations are optional, but if both are given, local declarations must be made first. If the block is the body of a globally defined operation-form or expression, a nonlocal declaration effectively declares its variables as global ones.
A block delimits a local environment. It allows new uses of names which do not interfere with uses of those names outside the block. For example, within a block, a predefined operation name can be redefined and used for a different purpose. Only the reserved words of Q’Nial cannot be reused in this fashion.
Definitions that appear within the block have local scope. That is, the definitions can be referenced only in the body of the block. Variables assigned within the block may or may not have local scope, depending on the appearance of a local and/or a nonlocal declaration. If there is no declaration, all assigned variables have local scope. Declaring some variables as local does not change the effect on undeclared variables that are used on the left of assignment. They are automatically localized.
If a nonlocal declaration is used, an assigned name that is on the nonlocal list is sought in surrounding scopes. If the name is not found, a variable is created in the global environment.
A list may be constructed directly by using
bracket-comma
notation. In this notation, the items of a list are separated by commas and the list is bounded by square brackets. If an item is omitted before or after a comma, then the fault value
?noexpr
is used for the value of the missing item. The notation denotes the
Null
if there are no items, and a solitary if there is only one item.
The execution of
Break
causes the interpreter to interrupt normal execution in an expression sequence and to display the current callstack. It then prompts for input with the prompt
-->
followed by the default command in brackets. The visible environment is that of the expression in which the break occurs. Thus, it is possible to examine the values of local variables in break mode.
At a break you can type any expression to inspect the value of a variable or to see a portion of its value. The operation
see
can also be used to view any of the definitions in the environment.
The debugging capability allows you to step forward in expression sequences using commands
step, stepin, next
or
toend
to control whether you step into or over other definitions or to the end of a loop or a definition. The command
resume
ends the break and normal execution is restarted.
In console versions of Q’Nial, break mode can also be entered by typing <Ctrl B> in response to the prompt for a
read
or
readscreen
operation or
Readchar
expression when in windows mode. In this case, after
resume
or
step
is entered, Q’Nial returns to the
read
or
readscreen
operation and awaits the user input. Break mode cannot be entered by typing <Ctrl B> in
editwindow
or when
setinterrupts
has been used to inhibit interrupts.
foo is op A { Break; A }
foo 3
-------------------------------------------------------------
Break debug loop: enter debug commands, expressions or
type: resume to exit debug loop
<Return> executes the indicated debug command
current call stack :
foo
-------------------------------------------------------------
?.. A
-->[stepv]
In the example, the operation
foo
has a
Break
which is executed when
foo
is applied to 3. If
Return
is pressed at the prompt the expression
A
is evaluated and 3 is displayed, followed by another prompt.
The operation
breakin
installs a break point prior to the first executable expression in a definition named by the string or phrase
Defname. The named definition must be an expression or an operation form. The effect is that when the definition is executed a break interrupt occurs prior to the execution of the first expression in the expression sequence in the body of the definition.
The optional argument
Mode
is provided to set the internal flag explicitly. If it is omitted, the internal flag value is toggled. All breakin flags are initially
false. The value of the breakin flag is retained if the definition is replaced by a
loaddefs
so that editing a definition and reloading it does not change its breakin status. However, if definitions are reloaded using a
restart
then the breakin status is set to
false.
The operation returns the previous setting. If the result of
breakin
is assigned to a variable, the previous setting can be restored later. The names of definitions that have breakin set can be viewed by the expression
Breaklist.
library"labeltab
see "labeltable
labeltable IS OPERATION Corner Rowlabel
Columnlabel Table {
% Combine the corner label with the column
labels for first line;
Firstrow := Corner hitch Columnlabel;
% Hitch the row labels to the rows of
the table;
Labeledrows := Rowlabel EACHBOTH hitch
rows Table;
% Hitch the first row of labels to the labeled
rows and mix them;
mix (Firstrow hitch Labeledrows) }
X gets count 3
1 2 3
# execute labeltable without break;
labeltable "TIMES X X (X OUTER times X)
TIMES 1 2 3
1 1 2 3
2 2 4 6
3 3 6 9
# set breakin and execute again
breakin "labeltable
o
labeltable "TIMES X X (X OUTER times X)
-------------------------------------------------------------
Break debug loop: enter debug commands, expressions or
type: resume to exit debug loop
<Return> executes the indicated debug command
current call stack :
labeltable
-------------------------------------------------------------
?.. Firstrow := Corner hitch Columnlabel
-->[stepv] TIMES 1 2 3
?.. % Hitch the row labels to the rows of the table
-->[stepv] resume
TIMES 1 2 3
1 1 2 3
2 2 4 6
3 3 6 9
The transformer
BYKEY
applies an operation
f
to lists gathered from
A, where each list is made up of items of
A
that have the same value in the corresponding position in
K. The result is a list with as many items as there are unique items in
K.
Str := 'a stitch in time saves nine';
cull Str
a stichnmev
Str BYKEY tally Str
2 5 3 2 4 1 1 3 1 3 1
Keys := 3 5 3 7 2 5 4 2 1 3 4;
Data := 23.1 14.2 13.5 18.9 22. 98. 3.5 28.7 19.3 16.5 43.2;
Keys BYKEY sum Data
53.1 112.2 18.9 50.7 46.7 19.3
average IS OP A { sum A / tally A }
Keys BYKEY average Data
17.7 56.1 18.9 25.35 23.35 19.3
In the first example
BYKEY tally
is used to count the frequency of the letters in the string
Str, giving the counts in the order of
cull Str. In the remaining examples, the values in
Data
corresponding to equal items in
Keys
are added and averaged in the two uses of
BYKEY.
 The result depends on what kind of name is provided, a phrase or a cast. If the name is provided as a phrase, the variable that is selected is determined by
assign
when it does the assignment by looking first in the local environment and then in the surrounding ones. If the name is provided as a cast, the variable selected is the one that exists at the point where the cast is formed. Thus, by-variable parameter passing is achieved by using the cast of the variable as an argument in the call. In the body of the operation the formal parameter is assigned using
assign
and evaluated using
value.
foo is op A Nm {
B := A + value Nm;
Nm assign (B + sum count 5); }
X := 100;
foo 1000 "X;
X
1115
A Prototype DLL calling interface has been added to the Windows versions of Q’Nial (Console, GUI and DLL version). This ability allows NIAL code to call external routines in DLL libraries. These libraries can be user written, part of the Operating System, or 3rd party DLLs.
The DLL interface routines manage an internal table that keeps track of the currently installed DLLs and their argument types and result type. This table also tracks if the actual library has been loaded (by a call to
calldllfun). The table is preserved as part of the workspace, and once DLL functions have been registered in a workspace, they are still available if the workspace is saved and subsequently loaded. The CallDLL facility assures that the DLL files are loaded and unloaded appropriately.
Every time a workspace is saved all DLL libraries are unloaded and the internal table is adjusted to reflect that. When a workspace is loaded, none of the DLL libraries will be loaded until a specific call to
calldllfun
brings in the DLL. This process assures that resources are not wasted on registered DLL calls that are never used.
The
registerdllfun
routine must always be called first to enter DLL function into the internal mapping table. This table keeps track of the name and DLL file for the routine and all of the argument types and function results. The DLL file is
not
loaded until the routine is called with the
calldllfun
routine. So errors involving paths to the DLL file or other related problems are not reported until the first call to
calldllfun.
The operation
calldllfun
provides an interface to an external routine implemented as a DLL for 32-bit Windows. The name
Nm
is the name associated with the routine when it was registered using
registerdllfun. The use of the name with appropriate arguments causes the DLL routine to be executed and its result (if any) returned as a Nial array.
The arguments
Arg0
...
ArgN
must agree in number and be compatible in type with the declarations made while registering the routine.
The details of argument passing and some examples are included in the entry for
registerdllfun.
The expression
Callstack
displays the sequence of active definition calls at the point it is invoked. It is usually used in conjunction with
Break.
Callstack
can be used to see the execution path by which the computation reached the current state while computation is suspended during a break.
There is a
canonical
way of displaying program text in Nial. This is done automatically by the routines
descan
and
deparse
used by
see. The canonical form sets the case of all identifiers used in Nial program text according to their role in order to assist visual parsing of Nial text. The following table summarizes the rules:
The operation
cart
corresponds to the cartesian product of set theory. Its purpose is to form all possible combinations of the items of its argument and return them in a structured result having as many axes as the sum of the number of axes of the items of argument.
In the above example, the shape of the result is 3 4 2, getting an axis of length 3 from the first item and axes of length 4 and 2 from the second.
 If the result matches one of the constants, C1 ... Cn, the corresponding expression sequence is executed. If the result does not match any constant, the expression sequence following else is executed.
Example:
Month := 3 ;
CASE Month FROM
1: Mname := 'January'; END
2: Mname := 'February'; END
3: Mname := 'March'; END
ELSE Mname := 'Year End';
ENDCASE ;
Mname
March
The use of the exclamation symbol
!
before an identifier causes Q’Nial to select the internal representation for the identifier rather than the value of the array associated with the identifier. Its use before a parenthesized program fragment selects the internal representation of the program fragment.
The major use of casts is in conjunction with the operations assign and apply. These operations mimic the Q’Nial constructs for assignment to a variable and application of an operation to an array. Casts permit passing an argument to an operation by variable name rather than by value. They also permit evaluation of a program fragment that has been stored in its internal form using the operation eval rather than requiring the use of the operation execute on the corresponding program text stored as a string.
Salary := 90000.
A gets 'Salary > 100000.' ;
Rule1 := execute A
o
Rule1 := eval !A
o
The details of the internal representation is not specified as part of the Nial language.
The cast notation
!Name
is used to denote the parse tree that represents the name. At the top level loop, parentheses must be included around the use of the cast notation, e.g.
(!Name), to avoid ambiguity with the use of
!
to indicate a host command.
The cast, because it is analyzed in the context in which it appears, refers to a variable or definition in a static way.
Q’Nial contains operations that mimic the underlying meaning of variables, expressions and operations in Q’Nial. The operations use strings, phrases or casts to represent the name of the object under consideration (except that see and getdef do not take casts).
Operation
Action
value A
Return the value of a variable named by string, phrase or cast
A
.
A assign B
Assign the array
B
to the variable named by string, phrase or cast
A
; return
B
.
A apply B
Apply the operation named by string, phrase or cast
A
to array
B
; return the result of the operation.
getdef A
Return the parse tree associated with the definition named by string or phrase
A
.
see A
Display the definition named by the string or phrase
A
.
update P A B
Put array
B
at address
A
in the variable named by the phrase, string or cast
P
; return the new value of
P
.
updateall P A B
Put items of
B
at addresses
A
in the variable named by the phrase, string or cast
P
; return the new value of the variable named by
P
.
deepupdate P A B
Put array
B
at path
A
in variable named by the string, phrase or cast
P
; return new value of the variable named by
P
.
The example joins two tables along the rows. Each table has two rows.
Definition
catenate IS OPERATION I A {
% "push down" I axis of items of A;
B := EACH ( I split ) A ;
IF equal EACH shape B THEN
I blend EACH link pack B
ELSE
fault '?conform error in catenate'
ENDIF }
The operation
char
is used to convert an integer in the range 0 to 255 to the character that has the integer as its internal representation. The result is system dependent.
char 66
B
char 66 67 68 69
BCDE
The major purpose of
char
is to create special characters for a specific system. For example, the characters that control cursor motion differ from one terminal to another. Programs using
char
may not be portable.
Equations
N in tell 256 ==> charrep char N = N
ischar C ==> char charrep C = C
The operation
charrep
is used to convert a character to its internal representation as an integer. This operation is system dependent.
charrep `A
65
charrep 'hello'
104 101 108 108 111
The major purpose of
charrep
is to permit determination of special characters for a specific system. For example, the characters that control cursor motion differ from one terminal to another.
Programs using charrep may not be portable.
Equations
N in tell 256 ==> charrep char N = N
ischar C ==> char charrep C = C
Checkpoint
is a user defined expression executed after a
save.
When a
save
is executed, the current computation is ended and the
Checkpoint
is done in the top level environment.
If a workspace being saved contains an expression named
Checkpoint, the expression is executed following the saving of the workspace and prior to restarting the top level loop. It can be used to restart a computational loop after an intermediate dump of the workspace.
The operation
choose
is used to select a subarray from array
A
specified by the array of addresses
I. The result is an array of the same shape as
I
with items chosen from
A. If an item of
I
is not an address of
A, the corresponding position in the result is the fault
?address.
The operation
choose
is related to the
at all
notation
V#I, which selects items from the array associated with the variable
V. The differences are that
choose
may select from an array that has not been assigned to a variable and that it handles out-of-range in a different manner.
3 1 0 1 3 4 choose 'range'
garage
The following example shows that the
valence
of the array of addresses can be higher than that of the array from which the selection is made.
I gets (2 4 reshape tell 8)
0 1 2 3
4 5 6 7
I choose 'some words as a string'
some
wor
Definition
choose IS OPERATION I A { I EACHLEFT pick A }
Equations
shape (I choose A) = shape I
J choose (I choose A) = (J choose I) choose A
tell shape A choose A = A
(list I) choose A = list (I choose A)
(EACH list I) choose A = I choose A
I allin grid A ==> EACH f (I choose A) = I choose EACH f A
Q’Nial organizes the data and code objects available for use into a logical structure called a
workspace
. It consists of a symbol table to hold associations between names and the predefined and user defined objects, a heap to store data and parse trees, a stack used to hold values temporarily during execution, and an atom table for uniquely storing phrases and faults.
When Q’Nial is invoked it starts the session with a clear workspace unless a specific saved workspace is requested or there is a
continue.nws
file in the local directory. The clear workspace is either found in the local directory, in the directory pointed at by
Nialroot. The name of the clear workspace is
clearws.nhs. If it is not found, then Q’Nial creates an internal clear workspace using its initialization process. A message indicates whether a clear workspace was loaded or created.
A user can tailor the initial state of Nial for their own purposes by saving a workspace with definitions in place as
clearws.nws
using
save.
The expression
Clearprofile
is used to clear the internal data structures that are used in the gathering of profiling statistics. It should be called when one profiling session has been completed and
profile
has been called, before starting another one.
A detailed explanation of the profiling mechanism is given in the help entry on profiling.
The expression
Clearws
clears the current workspace, erasing all variables and user definitions. It can cause loss of valuable information. The operation
save
should be used prior to using
Clearws
if the information in the workspace will be needed again.
The expression
Restart
is similar to
Clearws
except
Restart
re-initializes the interpreter according to the initial options given on the command line in a console version or set as defaults in a GUI version.
The operation
close
is used to close a file previously opened with the
open
operation. The argument is the file designator, an integer returned previously by
open
for that file.
The result of
close
is the fault
?noexpr
or a fault message indicating an error.
Fd := open "foo "w
3
close Fd
close Fd
?file is not open
In the examples above, the first use of
close
was successful. The second attempt to close the file resulted in the fault message.
The operation
cols
rearranges the axes of a table of shape
M
by
N
to form a list of length
N
of the columns of length
M
of the table.
Cols
is generalized to arrays of all valences as follows: if
A
is a list or a single, the result is
single A; if
A
has valence three or higher, the result is an array of valence one less than the valence of
A
with the second last axis pushed down.
If only column
I
of a table is needed, it can be selected by
I pick cols A
or by
A|[,I]
using the
at slice
indexing notation. The second way is more efficient for large arrays because it avoids restructuring the array. If a table
A
has no columns,
cols A
results in the empty list
Null.
 Comments may be placed anywhere in a block before or after declarations, definitions or expressions. Their purpose is to provide an explanation of the program fragment for the programmer who may be required to modify the program at a later date. The value of a comment as an expression is the
?noexpr
fault. Comments are retained when a definition is translated into internal form and they appear in its creation in the canonical form used by the operations see and defedit.
A
remark
is an input to the Q’Nial interpreter that is not processed. It begins with a line that has the symbol # as the first non-blank character in the line. In direct input at the top level loop, a remark ends at the end of the line unless a backslash symbol ( \ ) is used to extend the line. In a definition file, a remark ends at the first blank line. A remark cannot appear within a definition or expression-sequence.
The operation pack is used in evaluating binary pervasive and multi pervasive operations. Its task is to interchange the top two levels of the argument to such operations if the items of the argument
conform
. For the binary case there are two items; they conform if the items have the same shape, or if one or both items have only one item. In the latter case, the item with only one item is replicated to the shape of the other item.
For the multi pervasive case, all the items that do not have only one item must be of the same shape and all the items with one item are replicated to that shape.
The operation
content
returns the list of atoms of
A. The effect of content is to remove all structure from an array, returning a list of the atoms in a depth first, row-major order. The effect on an atom is to produce the solitary of the atom.
The expression
Continue
terminates a session of Q’Nial and returns control to the host environment. The workspace is saved in file
continue.nws
and is reloaded when Q’Nial is invoked at a later session using the same current directory.
If a
Restart
is executed in a session that began with a continue workspace, the current workspace will be reset to the continue workspace. If a session that began with a continue is terminated with
Continue, the file continue.nws is updated with the current workspace. If it is terminated with
Bye, the file
continue.nws
is deleted.
The file
continue.nws
is a workspace file that is created when a session is ended with the expression
Continue. When Q’Nial is invoked from the same directory, it will use
continue.nws
rather than
clearws.nws
as the initial workspace. If the Q’Nial invocation names a workspace, that workspace will take precedence over the continue workspace.
The transformer
CONVERSE
is used with a binary operation
f
and applies
f
to the pair formed by reversing the arguments of
CONVERSE f.
at IS CONVERSE pick
'abcde' at 2
c
holds IS CONVERSE in
count 20 holds 5
l
In the above examples,
at
and
holds
are defined in terms of
pick
and
in, respectively. The first example shows that
'abcde' at 2
gives the result expected for
2 pick 'abcde'. The second example shows that
count 20 holds 5
gives the same result as
5 in (count 20). Thus,
at
and
holds
do the same work as
pick
and
in; the former operations simply use their arguments in the reverse order.
 It differs from
tell
by counting from 1 instead of from 0.
The first example shows
count
generating the sequence of integers from 1 to 5. The second example shows how
count
can be used to generate a sequence of five real numbers with an interval of .1 between each number.
The operation
cull
returns a list whose items are those of
A
with duplicates removed. The order of the items is maintained.
cull 3 5 4 3 5 2 4
3 5 4 2
cull 'a few letters with duplicates'
a fewltrsihdupc
Definition
cull IS OPERATION A {
grid A EACHLEFT in (A EACHLEFT find A) sublist A }
Equations
cull A = list cull A
diverse A <==> cull A = list A
sortup cull A = cull sortup A
Pragmatics
The operation
cull
executes faster when a large array has been sorted, hence if the ordering of the result is unimportant it is better to use
sortup
to order the array prior to applying
cull.
A
curried-operation
is an operation in which the left item of a two-item argument is combined with a given operation to form an operation expression. Examples are:
1+
and
3 reshape.
A curried operation can be named or grouped in parentheses as an argument to a transformer.
The result of applying a curried-operation is determined by applying the simple-operation to the pair formed from the simple-expression and the argument to the curried-operation. Thus,
The operation
cut
converts an array
A
into a list of items formed from the items of
A
according to the bitstring
B. The list of items is divided where true values occur in the corresponding positions in the bitstring
B. The items of
A
where the divisions occur are not included in the items of the result and any empty segments are not included.
A := 'The boy stood on the burning deck' ;
` match A cut A
+---+---+-----+--+---+-------+----+
|The|boy|stood|on|the|burning|deck|
+---+---+-----+--+---+-------+----+
B := 'Formula 1: 3,5,7,,,9';
`, match B cut B
+------------+-+-+-+
|Formula 1: 3|5|7|9|
+------------+-+-+-+
EACH equal tell 4 4 cut tell 4 4
+-----------------+-----------------+-----------------+
|+---+---+---+---+|+---+---+---+---+|+---+---+---+---+|
||0 1|0 2|0 3|1 0|||1 2|1 3|2 0|2 1|||2 3|3 0|3 1|3 2||
|+---+---+---+---+|+---+---+---+---+|+---+---+---+---+|
+-----------------+-----------------+-----------------+
Cut
can be used to cut a string into a list of substrings. In the first example, the string is cut at blank characters. Several adjacent blanks are treated as one blank character. In the second example, the cut is done where commas occur. The third example shows that the items of
A
do not have to be atoms.
 The list of items is divided where true values occur in the corresponding positions in the bitstring
B. The items where the divisions occur are kept as the first item of each group.
A := 'The boy stood on the deck' ;
` match A cutall A
+---+----+------+---+----+-----+
|The| boy| stood| on| the| deck|
+---+----+------+---+----+-----+
B := 'Formula 1: 3,5,7,,,9';
`, match B cutall B
+------------+--+--+-+-+--+
|Formula 1: 3|,5|,7|,|,|,9|
+------------+--+--+-+-+--+
EACH equal tell 4 4 cutall tell 4 4
+---------------------+---------------------+---------------------+-----+
|+---+---+---+---+---+|+---+---+---+---+---+|+---+---+---+---+---+|+---+|
||0 0|0 1|0 2|0 3|1 0|||1 1|1 2|1 3|2 0|2 1|||2 2|2 3|3 0|3 1|3 2|||3 3||
|+---+---+---+---+---+|+---+---+---+---+---+|+---+---+---+---+---+|+---+|
+---------------------+---------------------+---------------------+-----+
Equations
tally (B cutall A) = sum (shape A reshape B)
B cutall A = (shape A reshape B) cutall A
list (B cutall A) = B cutall A
B cutall list A = B cutall A
The Q’Nial system provides an optional debugging facility that aids interactive debugging of definitions. It is active by default, but can be turned off for running production applications. See the detailed documentation for the various versions on how to turn off debugging.
The debugging system is based on the idea of placing breaks in the code and stepping through the program code in a number of different ways. Due to constraints in the way Q’Nial is implemented, debugging is always done in the context of an expression sequence. A break point occurs either before the execution of the expression sequence in a definition, or at an explicit break expression within an expression sequence. There is also a watch mechanism that executes a defined action whenever the value of a variable changes, and an ability monitor all use of user defined objects and of the predefined operations.
Defining a Break Point
There are two ways to cause a break in a Nial definition: by using the expression
Break
in an expression sequence, or by using the operation
breakin
to set a break on entry to the operation. The following table summarizes the break related primitives:
Expression
Action
Break
Suspend evaluation of the expression and pass control to an evaluation loop in the environment at the point of the break. Variables accessible at that point can be displayed. This loop recognizes a number of commands described below.
breakin Nm [M]
Set or rest an internal break flag for the definition of
Nm
. If the boolean value
M
is omitted, the flag is toggled. If set, a break occurs before the execution of the expression sequence of the definition. The
Nm
must be the name of a defined expression or a defined operation using the operation form style of operation expression.
Breaklist
Display the list of names of definitions with break flag set.
The operation
deepplace
returns an array the same as
A
except that the array at path
P
is replaced by
B. It is the insertion operation corresponding to the selection operation
reach
and generalizes the operation
place
from addresses to paths.
The operation
deepupdate
provides the semantics of the
Nm@@P := A
form of assignment expression.
Nm
must be an existing variable represented by a string, phrase or a cast;
P
is the path of addresses to the location to be updated; and
A
is the array to be placed in the variable.
The major purpose of
deepupdate
is to allow a selective update with a path on a global variable without forcing a copy. By passing the name of the variable to the operation that is doing the update, rather than its value, no sharing of the internal data is made and hence the update can be made "in place".
A
definition
in Nial is a syntactic construct that names a program fragment. The syntax is one of the three forms:
<name> IS <array expression>
<name> IS <operation expression>
<name> IS <transformer expression>
A definition is used to associate a name (identifier) with a program fragment that is an array expression, an operation expression or a transformer expression. If the definition appears within a block, the association is made in the local environment. Otherwise, the association is made in the global environment and assigns a role to the name as representing that kind of expression.
If the program fragment is syntactically correct, the name is associated with the program fragment in the environment and no result is given. If a syntax error is detected in the analysis of the program fragment, an explanatory fault message is returned and the name association is not made.
If the name being associated in a definition is already in use, the new definition must be for a construct of the same role and the earlier definition is replaced. The use of a defined name always refers to its most recent definition.
The operation
deparse
is used to convert a parse tree representation of a Nial definition or program fragment into a token stream which can be converted into text corresponding to the Nial definition. The argument to
deparse
must be either a cast or the result of
parse
or
getdef.
deparse !(Pi * cos 0.5)
99 1 ( 2 Pi 2 * 2 cos 18 0.5 1 )
The principal use of
deparse
is in displaying Nial definitions. It is used in the definition of
defedit
and
see.
The token stream returned by
deparse
also includes indicators to identify where new lines are to begin and to control indentation. The tokens corresponding to identifiers are given in the canonical form, indicating what role each token plays.
The result of
deparse
is implementation dependent and should be viewed as an internal representation provided as an interface to editing. It is subject to change as Q’Nial evolves.
Equations
Pt a parse tree ==> parse deparse Pt = Pt
Ts a token stream ==> parse deparse parse Ts = parse Ts
The operation
depth
returns an integer indicating the number of levels of nesting of the array. This is called the depth of the array. The depth of an atom is 0. The depth of a simple nonempty is 1. In general, the depth of an array is 1 plus the maximum of the depths of the items.
The operation
descan
converts a token stream to a list of strings that represents the program fragment given by the token stream. The input to
descan
must be a token stream produced by
scan
or
deparse
or an equivalent list of tokens.
descan scan 'A + 32'
+-------+
|A + 32 |
+-------+
descan deparse !(Pi * cos 0.5)
+-----------------+
|( Pi * cos 0.5 ) |
+-----------------+
The principal use of
descan
is in displaying Nial definitions. It is used to define
defedit
and
see. The argument to
descan
may also include tokens indicating the beginning of new lines and controlling indentation used to display a structured definition.
Equations
S a string of Nial text ==> scan link descan scan S = scan S
S a string of Nial text ==> canonical canonical S = canonical S
S a string of Nial text ==> execute canonical S = execute S
The operation
diagram
computes a character table that gives the fully boxed picture of
A
with the decoration of the atoms determined by the current setting of the decor switch. An array is displayed as a frame with cells for each item. It is arranged in two dimensions, using groupings of table frames to picture arrays of higher dimension. Each cell is large enough to hold the diagram of the corresponding item of
A.
The diagram of an atom is a picture that indicates its value. The diagram of a non-atomic single has an "o" in its upper left corner.
Diagram
returns the display given by the operation
picture
when in diagram mode of display.
The decor or nodecor mode switch controls the display of atoms. With decor set, it gives a picture that distinguishes all atoms.
The display of the result of a
picture
operation, such as
diagram, makes sense when it itself is pictured in sketch-nodecor mode. In other modes, the characters making up the table would be boxed and/or decorated.
Definition
diagram IS OPERATION A {
Old_setting := set "diagram ;
Result := picture A ;
set Old_setting ;
Result }
The number of axes of an array is referred to as its dimensionality. In array theory terminology the dimensionality is called the
valence
of the array. The following terms describe arrays by their valence:
The operation
display
returns a string which, when executed, returns the value
A.
set "decor; display 23.5
'23.5'
Display
inserts the operation to construct values that cannot be described directly by constants. In the example below, since the phrase containing a blank character cannot be specified simply using the phrase mark, display inserts the operation
phrase
and the string that will create the desired phrase
An answer.
In the definition of
X, strand notation was used. In the display of
X, brackets notation is created to represent
X. The display is seen to be correct by the last example that shows that the
execute
of the
display
of
X
is
X.
The operation
diverse
tests whether or not the items of
A
are all different. It returns
true
if they are and
false
if they are not.
diverse 2 3 5
l
diverse 'hello world'
o
diverse 2 3 (2 3)
l
In the last example, the items are all different because the last item is a pair of items (2 3) which is not the same as 2 or 3.

The operation
divide
returns the result of dividing two numeric atoms. It coerces the type of the atoms to be real and gives a real number result. If
B
is a numeric zero, the result is the fault
?div.
If one argument is numeric and the other is a fault or if both arguments are the same fault, the answer is the fault. In all other cases when one or more of the arguments is not numeric, the result is the arithmetic fault
?A.
DOWN is a general transformer that recurs over the depth of an array to some arbitrary level. DOWN has four operation arguments:
test
tests when the recursion has gone as deep as necessary,
endf
is applied to the argument that satisfies
test, structf
rearranges the argument before recurring on each item, and
joinf
combines the results of the recursion on the items.
DOWN [ atomic, 0 minus, pass, pass] 3 -4 (5 6)
+--+-+-----+
|-3|4|-5 -6|
+--+-+-----+
DOWN [ simple, dosum, pack, pass ] 3 4 5 6
18
DOWN [ atomic, 0 first, pass, 1 plus max ] [2, [3, 4, [5],8],24]
3
Definition
DOWN IS TRANSFORMER test endf structf joinf OPERATION A {
Candidates := [A];
Results := Null;
WHILE not empty Candidates DO
Candidates B := [front, last] Candidates;
IF B = "Start THEN
Candidates Shp := [front, last] Candidates;
N := prod Shp;
IF N = 0 THEN N := N + 1; ENDIF;
Results Items := opposite N [drop, take] Results;
Results := Results append joinf (Shp reshape Items);
ELSEIF test B THEN
Results := results append endf B;
ELSE
B := structf B;
Candidates := Candidates link [shape B, "Start] link reverse list B;
IF empty B THEN Candidates := Candidates append first B; ENDIF;
ENDIF;
ENDWHILE;
first Results }
Equations
DOWN [test, endf, structf, joinf ] A
= FORK [test, endf, joinf EACH DOWN [test, endf, structf, joinf] structf ] A
opposite A = DOWN [ atomic, 0 minus, pass, pass] A
sum A = DOWN [ simple, dosum, pack, pass ] A
depth A = DOWN [ atomic, 0 minus, pass, pass] A
The operation
drop
selects the items of an array after a specified number of items have been dropped. If
B
is a list and
A
is a non-negative integer, the result is the list formed from dropping
A
items from the front of
B. If
A
is a negative integer, the result is formed by dropping
abs A
items from the right end of
B.
If
B
is a table and
A
is a pair of non-negative integers, the result is lower right corner of
B
that remains after dropping the number of rows and columns indicated by
A
from the upper left corner of
B. If one or both items of
A
are negative, the dropping occurs from the other end of the extent of the corresponding axis.
For a higher dimensional array
B, tally A
must equal
valence B; and the result is obtained by dropping from the front or back of the extents along each axis. If
B
is a single and
product A
is zero, the result is
B; otherwise the result is an empty array with valence of tally
A.
2 drop "Able "Baker "Charlie "Dog
Charlie Dog
T1 := tell 2 4
+---+---+---+---+
|0 0|0 1|0 2|0 3|
+---+---+---+---+
|1 0|1 1|1 2|1 3|
+---+---+---+---+
1 2 drop T1
+---+---+
|1 2|1 3|
+---+---+
1 -2 drop T1
+---+---+
|1 0|1 1|
+---+---+
Definition
drop IS OPERATION A B {
IF not and EACH isinteger A THEN
fault '?left arg of drop must be integers'
ELSEIF valence B = 0 THEN
IF product A > 0 THEN
(tally A reshape 0) reshape B
ELSE
B
ENDIF
ELSEIF tally A ~= valence B THEN
fault '?valence error in drop'
ELSE
((A < 0) + (-1 * (A >= 0) )) * (shape B - abs A max 0) take B
ENDIF }
Equations
0 times shape B drop B = B
tally B drop list B = Null
The operation
dropright
is an obsolete operation that drops items from the ends of extents. It is provided to retain compatibility with earlier versions of Q’Nial.
The transformer
EACH
forms an operation, called the
EACH
transform of
f, that applies
f
to every item of the argument
A. The shape of the result of the application of
EACH f
to
A
is the same shape as
A.
EACH reverse ('abc' 'def' 'ghi')
+---+---+---+
|cba|fed|ihg|
+---+---+---+
EACH first tell 3 4
0 0 0 0
1 1 1 1
2 2 2 2
EACH
is used to distribute an operation
f
across an entire array
A. Its use avoids the need to explicitly allocate space for the result and to write an explicit loop to apply
f
to each item of
A
individually.

When the items of
A
are not all the same shape,
A
is examined to see if all the items with tally greater than one have the same shape. If so, all the items of tally 1 are replicated to that shape. If not, the fault
?conform
is returned.
EACHALL
is implicitly applied in multi pervasive operations.
The transformer
EACHBOTH
forms an operation which applies
f
to pairs formed by selecting the items of
A
and
B
in corresponding positions, assuming that
A
and
B
have the same shape.
When
A
and
B
do not have the same shape, if either
A
or
B
has only one item, that item is replicated to the shape of the other argument; otherwise, the fault
?conform
is returned.
EACHBOTH
is used implicitly in all binary pervasive operations.
The transformer
EACHLEFT
forms an operation which applies
f
to pairs formed by pairing the items of
A
with
B.   The editor used is determined by the environment variable
EDITOR
or by a default chosen for each version. When the editing task is completed, the editor returns control to Q’Nial. The result is the fault
?noexpr.
edit "test.ndf
edit 'data_records'
If
edit
is used to edit a file of Nial definitions, the operation
loaddefs
must be executed on return from the editor to load the definitions.
The operation
empty
tests whether or not an array
A
has any items. If
A
has no items, it returns
true; otherwise,
false. The predefined expression
Null
is empty as is any array with a zero in its shape.
Since there are empty arrays with more than one dimension, it is better to test for an empty array by using
empty
rather than by direct comparison with
Null.
empty Null
l
empty (0 3 2 reshape 5)
l
empty solitary 5
o
empty solitary Null
o
The last two examples show that a solitary is not empty even if the item it contains is empty.
The operation
equal
is normally used to compare two arrays
A
and
B
to see whether or not they are identical. It returns
true
if they are equal;
false
otherwise. (Two nonempty arrays are identical if they have identical shapes and hold identical items at each location. Two empty arrays are identical if they have identical shapes.)
Empty
is extended to arbitrary arrays by returning
true
if all items of an array are identical.
The symbol = is a synonym for equal and can be used in both infix and prefix application. In the
Equations
sections of the dictionary entries, the symbol = is used to separate two sides of an equation. To test such an equation, it may be necessary to replace the expression following = with the same expression in parentheses in order to force correct parsing of the equation as a Nial expression.
2 3 4 = [2,3,4]
l
Null = ''
l
2 (3 4) = (2 3) 4
o
equal EACH shape tell 2 3 4
l
The first example shows that a triple formed using strand notation is identical to that formed using brackets notation. The second shows that
Null
is equal to the empty string. The third shows that grouping items of a list in different ways creates different arrays. The last shows the convenience of the more general form of
equal.
Equations
A equal B <==> shape A = shape B
A equal B and (I in grid A) ==> I pick A = I pick B
The equations in the Nial Dictionary and in the on-line help provide an abbreviated way of stating properties of the term or object being described. They could be expanded into an explanation in English but that would lengthen the manual considerably.
The equations use variables such as
A, B
and
C
that take on array values; and variables such as
f, g
and
h
that denote operations that map arrays to arrays. An equation holds for all arrays and for all computable operations without side effects, unless a specific qualification is made. Thus, the equation:
(EACH f) (EACH g) A = EACH (f g) A
says that for all operations
f
and
g
and all arrays
A, the use of the
EACH
transform of
f
on the result of the use of the
EACH
transform of
g
on
A
has the same value as the use of the
EACH
transform of the composition of
f
and
g
on
A. In mathematical terms, the
EACH
transformer distributes over operation composition.
The symbol
=
is used in its mathematical sense in equations and separates two Nial expressions. The symbol
=
has higher precedence than the two expressions it is separating. Where the Nial equivalent to
=
is needed to state the equation, the term
equal
is used.
To test an equation using Q’Nial, in order to force the correct parsing of the equation as a Nial expression, it may be necessary to replace
Expr1 = Expr2
with
Expr1 = ( Expr2 )
The equality used in equations assumes that both sides of the equations compute without triggering a fault and produce equal arrays; or both produce the same fault value if fault triggering is off. In some cases, the equality is inexact due to roundoff errors.
The symbol
=f=
is used to denote an equality where both sides produce the same non-fault value; but in some cases one or both of the sides may fault and the equality is no longer valid.
Some of the equations are qualified by a constraint on the variables. The constraint is written in English or as a conditional expression in Nial that must hold for the equation to be true. In a statement of the form
Expr ==> Eqn
Expr is the qualification written as a Nial boolean expression, the symbol ==> is used for "implies", and Eqn is the equation that holds under the qualification.
The symbol <==> denotes an if and only if implication. Thus a statement of the form
Expr1 <==> Expr2
states that both expressions have the same truth value; either both are true or both are false.
Reading the Equations
The equations related to
abs, the operation that finds the absolute value of a number, are:
abs A = EACH abs A
shape abs A = shape A
abs abs A = abs A
The first two come from the property that
abs
is
unary pervasive. The first one says that applying
abs
to an array
A
is the same as applying
abs
to the items of array
A. It also implies that the shape of the result of
abs A
is the same as shape of
A
since
EACH
transforms always preserve shape. The second equation says that the shape of the result of
abs A
is the same as the shape of
A. The third equation indicates that subsequent applications of
abs
after the first do not change the result.
The equations related to
minus, denoted by
-, are:
A - B = EACH - pack A B
shape (A - B) = shape pack A B
A - B = A + opposite B
The first equation says that pair of arrays
A
and
B
are packed at each level of nesting to bring corresponding items together. Then, the items are subtracted.
The second equation states that the shape of the result of subtracting
A
from
B
is the shape formed by packing
A
and
B. The third equation states that subtracting
A
from
B
is equivalent to adding the opposite of
B
to
A.
The equations related to
hitch
are as follows:
A hitch B = A hitch list B
list (A hitch B) = A hitch B
The first says that
hitch
treats its right argument as though it were a list, and the second states that the result of
hitch
is a list.
The following equations illustrate the use of qualifications:
atomic A ==> single A = A
not empty A ==> mix rows A = A
diverse A <==> cull A = list A
The first equation says that if
A
is atomic, the single of
A
is equal to
A. That is, the single of an atom is the atom. The second equation says that if
A
is not empty, forming the rows of
A
and applying
mix
to recombine them, results in the original array. The third equation says that if
A
is diverse, the cull of
A
is equal to the list of
A; and that if
A
is not diverse, the cull of
A
is not equal to the list of
A.
There are many identities that hold for all arrays in Nial. The equations are shown in the on-line help for each topic.  The argument
Nm
must be a phrase or string giving the object to be removed. Predefined names and names in local scopes cannot be removed.
When a name has been erased, it remains in the symbol table with its original role. It can be redefined in the same role or, if a variable, reassigned a value by a subsequent action. A name cannot change its role once established.
average IS divide[sum,tally];
calc IS EACH average;
Var3 := calc (2 3 4) (5 6 7)
3. 6.
EACH erase "average 'Var3' "sum
?noexpr ?noexpr ?system name
In the example above, the operation
sum
cannot be erased as it is a predefined operation. In the next three examples below, faults are returned because of missing variables or operations that were erased. The effect of erasing an object is not propagated to other definitions referring to the erased name.
Var3
?no_value
average count 10
?missing_op
calc (2 3 4) (5 6 7)
?missing_op ?missing_op
average := 5 6 7
?invalid assignment: AVERAGE := <***> 5 6
In the last example, an attempt is made to redefine the role of
average
and Q’Nial indicated that
average
is not a suitable name for such an assignment.
The operation
eraserecord
is used to erase component
N
of direct access file designated by the number
Filenum. In a file with record components,
eraserecord
replaces the erased component with an empty string. In a file with array components, it replaces the erased component with the fault
?missing.
N
may be a single record number or a list of record numbers.
If the last component in a file is erased, any immediately preceding components that are empty in a record component file or contain the fault
?missing
in an array component file are also removed. The file tally, which records the number of records in the file, is adjusted accordingly.
The operation returns the fault
?noexpr
or a fault indicating improper use of the operation.
File_num := open "Names "d ;
eraserecord File_num 23 ;
The operation
eval
provides access to the underlying expression evaluator of Q’Nial. The argument to
eval
must be a parse tree returned by
parse
or
getdef, a named expression or a cast which represents a Nial expression. The effect is to evaluate the expression and return the resulting value. As seen below, the parse tree can be one formed by using the cast mechanism or one built explicitly using
parse.
eval parse scan '23 + 45'
68
eval !(23 + 45)
68
The next example shows that
eval
can be used to evaluate a named expression using either the cast or the phrase of its name.
A := 100 ;
Addone IS (A := A + 1);
(eval !Addone)(eval "Addone)
101 102
(eval !A)(eval "A)(value "A)
102 ?not an expression 102
The last example shows that
eval
can be used to evaluate the cast of a variable but not a phrase naming the variable. The operation
value
should be used instead.
Equations
S a string holding a Nial expression ==> eval parse scan S = execute S
Pt a parse tree ==> eval parse deparse Pt = eval P
The operation
except
returns the list of items of
A
that are not in
B. It corresponds to set difference if
A
and
B
are viewed as set representations. The result is a list with items in the same order as in
A.
The last two examples show that if the left argument has nonatomic items, to remove a single non-atomic array, the right argument must be made into a solitary list. Otherwise the items of the right argument are used.
Definition
except IS OPERATION A B { A EACHLEFT notin B sublist A }
Equations
list (A except B) = A except B
list A except list B = A except B
(A except B) except C = A except (B link C)
A except cull B = A except B
Pragmatics
The operation
except
uses an internal sort on its arguments to reduce the algorithmic complexity of the above definition. It executes considerably faster if its arguments have already been sorted.
The operation
execute
evaluates a Nial expression given as a string
S. The result of the evaluation is returned. The execution of the string takes place in the environment where execute is applied.
If the string contains a name, the local meaning of that name will be used if a local meaning exists. If execution of the string results in the creation of a new variable or defined name, the new object is placed in the global environment.
If a string being executed contains an integer representation that would be converted to an integer outside the range for the computer, the result is converted to the corresponding real number.
execute '23 + 40045'
40068.
A := count 5; opname := 'product';
execute link opname ' A'
120
The last example shows that Nial program text can be constructed under program control and then executed. This feature is useful in applications where responses are generated by combining text elements based on the user input and the current state of the computation. The technique is useful for applications involving computer assisted instruction and knowledge based systems.
It is more efficient to use
eval
to evaluate the cast of an expression than to use
execute
to evaluate the corresponding string. In the former approach the
scan
and
parse
is done once, whereas in the latter it is repeated on each execution.

The term
expression
is used in its most general sense to describe a program fragment that denotes one of the three primary objects of Nial: an
array, an
operation, or a
transformer. However, in most contexts we use the term as an abbreviation of an
array expression.
An array expression denotes an array value. That is, it is a program fragment that when evaluated in the proper context will produce an array. The predefined expressions of Q’Nial either produce a constant value, or they carry out some system action and return the fault value
?noexpr.
The control constructs of Nial are array expressions made up of keywords, simple-expressions and expression-sequences.
A named expression is either a predefined expression or an expression that has been given an explicit name using the
IS
definition mechanism.
An
expression-sequence
is the main construct used for program text that produces a value. It consists of one or more expressions separated by semi-colons and possible followed by a semi-colon.
The expressions in an expression-sequence are evaluated in left-to-right order. If the sequence does not terminate with a semicolon, the array returned is the result of the last expression. If the sequence does end with a semicolon, the array returned is the fault
?noexpr. At the top level loop, if the array returned is the fault
?noexpr, it is not displayed.
The term
extent
is used to describe the length of an axis of an array in a particular dimension. Thus a 4 by 6 table is said to have extent 4 in the first dimension and extent 6 in the second dimension.
An external declaration assigns a role to a name, allowing it to be used in a definition before its own definition is given. This mechanism is useful for creating mutually recursive definitions. An external declaration is made only in the global environment.
If the name is already defined with the same role, the declaration has no effect. If the name has another role, a fault is reported. If the name is not currently defined, a default object is associated with it.
The constant expression
False
denotes the boolean atom for
false, which Nial also denotes by
o. It is the result of comparing two arrays that are not identical for equality.
False (not False)
ol
Equations
tally False = 1
shape False = Null
single False = False
not False = True
The operation
fault
converts a string or phrase into the fault value with the string as its message. If the argument is not a string, phrase or a fault, the result is the fault
?type error.
Fault
is used to construct faults that contain blanks or other characters than cannot appear in a literal fault or ones constructed from a message provided from the host system.
By default, fault triggering is on when the interpreter is initialized. This causes the creation of a fault to interrupt the flow of execution. The operation
quiet_fault
can be used to create a fault value without triggering an interrupt.
Nial assumes that every computation that terminates results in an array value. However, there are many cases where a computation does not have a sensible answer. If division by zero occurs, for example, there is no suitable number to return. Nial uses special atomic arrays called faults to indicate such results. For division by zero it is ?div.
Q’Nial has two ways of handling a fault: either a trigger mechanism is executed that causes an interruption when a fault is created, or during execution of a defined operation, expression or transformer the fault is treated as a normal atomic array.
When Q’Nial is invoked, the fault triggering mechanism is turned on by default. (This effect can be suppressed by using an option in the setup for each version). During execution, the state of the triggering mechanism can be turned on or off using the operation
settrigger. The operation
quiet_fault
can be used to create a fault without causing fault triggering.
If fault triggering is set and a fault is generated during execution of a defined operation, execution is interrupted. On an interruption caused by a fault, a display message appears giving the call stack of definitions currently executing and the line of text that caused the fault. For example, the definition:
foo is op A B { A / B + 1 }
followed by the evaluation of the expression
foo 3 0
results in the output:
-------------------------------------------------------------
Fault interruption loop: enter expressions or
type: <Return> to jump to top level
current call stack :
foo
?div triggered in : ... A / B
-------------------------------------------------------------
>>>
where the string '>>>' is a special prompt indicating that a fault has occurred and execution has been interrupted. The prompt permits you to query the value of variables in the expression and its surrounding computation or to view the operation that has triggered the fault. The above session might continue as:
>>> see "foo
foo IS OPERATION A B {
A / B + 1 }
>>> A
3
>>> B
0
>>>
A variable in a definition that called the current one can be referenced by preceding the variables name by the definition name and a colon, e.g.
G:X
denotes variable
X
in definition
G. You can execute any expressions you want at the prompt. A useful thing to do is to see the definition that has interrupted. When you are ready to resume, reply to the prompt with a Return and control returns to the interactive loop.
A
fault
is a special kind of atomic value used by Q’Nial to signal special values or to indicate that an operation has been given an argument it cannot handle in a normal way. The special value faults are:
The operation
filelength
returns the length in bytes of the host file named by string or phrase
Filename. It is used in conjunction with
readfield
and
writefield
in processing host files.
Len := filelength "Myfile;
Data := readfield "Myfile 0 Len;
In the example,
filelength
is used to determine the size of the file and
readfield
is used to read it in as raw byte data. If the file corresponds to a text file, the data will include end of line indications appropriate for the host system.
The operation
filepath
converts a list of phrases or strings giving the names of directories and a filename into the system dependent string that access the specific file. This operation is useful in writing system independent programs that will work with either UNIX or DOS (and WINDOWS) path name conventions.
Example:
filepath ".. "aitools "newfnfl "frames
returns the string
../aitools/newnfl/frames
on a UNIX version of Q’Nial, and returns
..\aitools\newnfl\frames
on a PCDOS version.
Definition
filepath is op Nms {
front link (EACH string Nms EACHLEFT append Separator ) }
The expression
Filestatus
gives information on the files currently open in a Q’Nial session. It returns a list of triples, one for each open file, giving the file number as an integer, the filename as a phrase and the mode as a character.
The modes are
r, w, a, d, pr, pw
and
c, standing for
read, write, append, direct, pipe_read, pipe_write
and
communications
respectively.
The files for standard input, standard output and standard error are opened with modes of r, w and w respectively using file numbers 0, 1 and 2.
Fnum := open "F "a;
Filestatus
+---------+----------+----------+-----+
|0 stdin r|1 stdout w|2 stderr w|3 F a|
+---------+----------+----------+-----+
The operation
filetally
returns the number of records in the file designated by
Filenum, an integer returned by an earlier call to
open. The filetally is one higher than the highest component number of a record or array that is written but not erased.
The transformer
FILTER
is given a predicate operation
f
that is used to select items from
A. The result of applying
FILTER f
is to produce a list of the items of
A
that satisfy the predicate
f.
The operation
find
returns the address of the first occurrence of
A
as an item of
B, searching
B
in row major order. If
A
does not occur in
B, the result is the
gage
of
shape B. The result of
find
is an integer if
B
is a list; and a list of integers of tally equal to
valence B
otherwise.
In the second example, the character a is not in the string 'hello world' and the result is the tally of 'hello world'. In the last example, the result is a list of two integers because the valence of count 3 4 is 2.
Definition
find IS OPERATION A B { gage first ( A findall B append shape B ) }
Equations
A seek B = A [in,find] B
A in B ==> A find B pick B = A
I in grid A and diverse A ==> I pick A find A = I
Pragmatics
The operation
find
uses a linear search on the items of
B
if the array has not been sorted, or uses a binary search algorithm if it has. The latter fact suggests that an array that is searched frequently should be kept in lexicographical order by applying
sortup
to it when it is created or changed.
The operation
findall
returns a list of the addresses of all occurrences of
A
as an item of
B, searching
B
in row major order. If
A
does not occur in
B, the result is the empty list
Null.
The result of
findall
is a list of integers if
B
is a list and a list of pairs if
B
is a table.
In the first example
findall
returns the list of integers where 3 is found in the list; in the second a list of pairs is returned. The last example shows that if only one occurrence is found the result is a solitary list.
Definition
findall IS OPERATION A B { A EACHRIGHT equal B sublist grid B }
Equations
(list A) findall B = A findall B
list (A findall B) = A findall B
A in B ==> A find B = first (A findall B)
Pragmatics
The operation
findall
uses a linear search on the items of
B
if the array has not been sorted, or uses a binary search algorithm if it has. The latter fact suggests that an array that is searched frequently should be kept in lexicographical order by applying
sortup
to it when it is created or changed.
The transformer
FOLD
modifies an operation
f
to one that takes a pair of arguments consisting of an integer
N
and an arbitrary array
A. The result of applying
N FOLD A
is to apply
f, N
times, applying it first to
A
and subsequently to the result of the previous application.
The transformer
FORK
implements a conditional functional mechanism corresponding to the if-expression. The argument to
FORK
must be an atlas of length two or more. The operation in the first position is a predicate. If there are exactly three items in the atlas, the first is a predicate; the second is applied to
A
if the predicate returns
true; and the third is applied to
A
if the predicate returns
false. That is, if
f A
is
true, the result is
g A; otherwise it is
h A.
If there are more than three operations in the atlas, they are taken in pairs from left to right. The first of each pair must be a predicate and is applied to
A
in turn until a result is
true. The result of the transform is the result of applying the second of that pair to
A. If no application results in
true
and the number of operations in the atlas is an odd number, the last operation is evaluated. Otherwise the result is the fault
?noexpr.
The
FOR-loop
control structure is used to execute the expression sequence
ExpSeq
repeatedly while variable
Var
takes on the values specified as items in the simple expression
Exp.
FOR X WITH 1. 2. 3. DO
write (X) (X*X) (X power X) ;
ENDFOR
1. 1. 1.
2. 4. 4.
3. 9. 27.
In the example, X takes on the values 1., 2. and 3. in successive loops and the values of X, X squared and X to the power X are displayed.
simple A and and (type A match type first A) => A = fromraw (toraw A) (first A)
Pragmatics
Fromraw
is used to convert raw bit data into atomic data that can be manipulated by Nial. If the data was created from Nial data using
toraw
then it will work in a system independent way. However, if raw byte data is obtained using
readfile
and then converted using
toraw
the host system byte ordering may need to be allowed for.
The operation
front
returns a list of all items but the last of
A.  In Nial, an
operation
is an object in the set of functions from the domain of Nial arrays to the range of Nial arrays. Thus, an operation always applies to an array and returns an array.
A
transformer
in Nial is also a function. It domain is Nial operations and its range is also Nial operations. Since its argument is itself a function, a transformer is said to be a
second order
function. A transformer always applies to an operation and results in an operation.
Definitions in which the associated object is a simple-expression are used to name program fragments that return an array value but which do not need parameters. The resulting named-expression behaves like a function having no parameters.
Nial is considered to be a functional language, but it is not purely functional in that it has assignments, loops and other non-functional concepts.
The operation
fuse
is used for two distinct purposes. If
I
is simple and contains all the axes of
A
without repetition, the result is an array formed by a permutation of the axes of
A
by
I. The shape of the result is
I choose shape A. If
I
is not simple but link
I
is simple and contains all of the axes of
A
without repetition, the result is obtained by diagonalizing along axes that are grouped together, ordering them according to the ordering in
I.
If link
I
does not contain all the axes or if there are repetitions of the axes in link
I, the fault
?invalid fuse
is returned.
The operation
gage
is used to convert an array of integers into an atomic integer if there is only one item or a list if there is more than one. The integers must be non-negative otherwise the fault
?gage
is produced.
The main purpose of
gage
is to express a list of non-negative integers in the form an address takes, i.e. it converts a solitary integer to the integer itself and leaves all other lists alone.
Definition
gage is OPERATION A {
IF and EACH isinteger A THEN
IF tally A = 1 THEN
IF first A >= 0 THEN first A ELSE ??gage ENDIF
ELSE
IF and EACH (0<=) A THEN list A ELSE ??gage ENDIF
ENDIF
ELSE
??gage
ENDIF }
The operation
getdef
retrieves the parse tree associated with the global definition named
Nm.
Nm
may be a phrase or a string. Only the parse trees associated with global user definitions can be retrieved.
The example shows a parse tree for a simple definition. For definitions of any reasonable size the diagram of the parse tree becomes too large to examine easily.
The primary use of
getdef
is to provide an interface between internal representations and the editing facilities. It is used in
see
and
defedit. The detailed form of the parse tree is implementation specific and may change in future releases.
The operation
getfile
returns the records of the text file named by the string or phrase
Filename
as a list of strings. The file must not be open.
Recs := getfile "Myfile;
Definition
getfile IS OPERATION Filename {
Fnum := open Filename `r;
IF isfault Fnum THEN
Fnum
ELSE
Lines := '';
Line := readfile Fnum;
WHILE Line ~= ??eof DO
Lines := Lines append Line;
Line := readfile Fnum;
ENDWHILE;
Lines
ENDIF }
The operation
getname
retrieves the variable or definition name associated with a name reference
triple
within a parse tree. The argument must be a triple with first item 2 that has been produced by the Q’Nial parser. The operation is useful when analyzing the structure of a parse tree representation. This is a specialized task and
getname
is used only with considerable knowledge of the internal workings of Q’Nial.
The operation
getsyms
retrieves the parameters and local variables of a defined operation or expression named by
Nm. The operation is useful when analyzing the name usage of a definition in the context of analyzing the name interaction among a set of definitions. This is a specialized task and
getsyms
is used only with considerable knowledge of the semantics of Q’Nial.
The
global environment
is the set of associations between names and objects formed in the workspace that are either predefined in Nial or have been created by actions that have taken place during a session.
The transformer
GRADE
modifies a comparator
f
to produce an operation that, when applied to
A, returns an array of addresses of the same shape as
A
that orders
A
according to the comparator. The addresses can be used to select the items of
A
(using
choose) so that the items are in order according to
f.
A
comparator
is an operation that compares two arrays and returns
true
if they are in the desired ordering or
false
otherwise. Operations
lte
(
<=) and
gte
(
>=) are the most commonly used comparators.
The first three expressions illustrate that
GRADE
returns the addresses that re-order the items of table
A
in descending order. The next expression shows that the
GRADE
transform of
<=
can be applied to a list of phrases. The second last expression shows that
GRADE <=
cannot be applied to a list of strings. This is because
<=
is used itemwise on the characters of the strings and hence the comparator yields a bitstring rather than an atomic boolean result. The last expression shows that the comparator
up
can be used with strings.
 The result has the same shape as
A. Each item of the result is the address of the corresponding item in
A. The grid of a list is a list of integers. The grid of a table is a table of pairs of integers.
The operation
gt
compares two atoms
A
and
B
with the
greater than
relation, returning
true
if
A
is greater than
B
and
false
otherwise. The symbol
>
is a synonym for
gt.
The atoms in Nial are organized as a lattice using
<=
for the ordering. The numeric atoms are comparable across types but numeric and literal atoms are incomparable. The literal types are not comparable across types. A comparison between incomparable objects results in
false.
R := l 2 2.5 `a "abc ??error
l 2 2.5 a abc ?error
R OUTER > R
oooooo
looooo
lloooo
oooooo
oooooo
oooooo
'apple' > 'above'
olloo
"apple > "above
l
The use of
OUTER >
shows the comparisons between various atom types. The last two examples show the difference between comparing two strings, where the operation is distributed by its pervasive property; and comparing the corresponding phrases.
Definition
gt IS OPERATION A B { (A gte B) and not (A mate B) }
The operation
gte
compares two atoms
A
and
B
with the
greater than or equal
relation, returning
true
if
A
is greater than or equal to
B
and
false
otherwise. The symbol
>=
is a synonym for
gte.
The atoms in Nial are organized as a lattice using
<=
for the ordering. The numeric atoms are comparable across types but numeric and literal atoms are incomparable. The literal types are not comparable across types. A comparison between incomparable objects results in
false.
 The last two examples show the difference between comparing two strings, where the operation is distributed by its pervasive property; and comparing the corresponding phrases.
The Q’Nial Help Facility is integrated with the Windows Help mechanism in the Q’Nial for Windows version. If the cursor is over a word for which there is a help entry, then pressing F1 will bring up the help screen for that topic.
There is no Help Facility in console versions. However the Windows help file
Winnial.hlp
can be run independently of WinNial.exe.
The first example shows that the list 2 3 4 becomes an item on the front of the list 5 6 7. In the next example, the right argument of
hitch
is treated as a list. The last example shows that if the right argument is an empty list, the result is the left argument as a solitary.
 The argument
S
is a string or a phrase. If the action carried out by the host system produces output, the output is displayed on the screen. In window mode, Q’Nial is unable to capture this output and hence it may scramble the screen output. The screen is restored by executing
Refresh.
The result of
host
is the fault
?noexpr
if the command has returned normally; or a fault generated from a system dependent error message supplied by the host operating system. At the top level loop, a line beginning with
!, the exclamation mark, is interpreted as a host command.
The
IF-expr
construct is a notation for executing one of a number of possible expression sequences
Es1, Es2
, ...
Esn. The sequence selected depends on the result of the conditional expressions
C1, C2
, ...
Cn. In the general case, whichever condition is first found to return
true
specifies the expression sequence to be performed. If all the conditional expressions return
false, expression sequence
Esx
is selected. The
ELSEIF
and
ELSE
clauses are optional.
In the following example, the result is one of phrase
Adult, Minor
or
Juvenile, depending on the value of
Age.
Age := 17;
IF Age > 18 THEN
"Adult
ELSEIF Age < 16 THEN
"Minor
ELSE
"Juvenile
ENDIF
Juvenile
The operation
in
returns
true
if
A
is an item of
B
and returns
false
if it is not.
3 in 56 34 23 3 57 3
l
1 1 in tell 3 4
l
`a in 'hello world'
o
`a in "apple
o
`a in string "apple
l
The fourth and fifth examples show that a letter is not an item of a phrase but is an item of the corresponding string.
Definition
in IS OPERATION A B { or (A EACHRIGHT equal B }
Equations
A in B = A in (list B)
A in Null = False
A in solitary A = True
A in (A hitch B) = True
A in (B append A) = True
A in (A pair B) = True
Pragmatics
The operation
in
uses a linear search on the items of
B
if the array has not been sorted, or uses a binary search algorithm if it has. The latter fact suggests that an array that is searched frequently should be kept in lexicographical order by applying
sortup
to it when it is created.
The term
indexing
is used to describe notations that can be used to
select from
or
insert into
a variable.
There are four indexing methods in Nial:
at,
at all,
at path
and
slice
represented by
@,
#,
@@
and
|
respectively. The different indexing methods return different subsets of the array. The following is a summary of the indexing methods:
The following example shows the slightly different structure which occurs when a comma is either present or missing before the last item. The library operation
findpaths
is used to indicate the path to the integer 10 in each case.
The index used in selecting a part of an array must be an expression that evaluates to a valid address. An invalid index returns a fault as follows:
Indexing Method
Fault
A@I
?address
A#I
?addresses
A@@P
?path
A|I
?slice
The operation
pick
works the same as the
at
method of indexing. If the index is invalid,
pick
returns the fault
?address. Similarly,
choose
works the same as
at all
indexing.
In Nial an operation-expression may be placed between two array-expressions.This is called an
infix
use of the operation-expression.
7 + 5
12
2 3 reshape 1 2 3 4 5 6
1 2 3
4 5 6
In using the infix notation, one must understand that if a sequence of operations are placed between two array arguments, all but the first operation are applied to the second argument.
The
initial
subdirectory is within the directory identified as the Nialroot directory. It holds a number of files that may be used for initializing Q’Nial including the
defs.ndf
file used for creating the clearws workspace.
When a Q’Nial session is initiated, if none of the -defs, Filename or Wsname options is used as a parameter to the nial command, the
clearws.nws
workspace is loaded. If the
clearws.nws
file does not exist in the current directory, it is sought in the Nialroot directory. If it is not present in either place, it is created automatically by the initialization process and kept internal to Q’Nial.
A user can customize their version of Q’Nial by setting up an
initial
directory and modifying the
defs.ndf
file to include definitions they wish to be present in every Q’Nial session.
The transformer
INNER
generalizes the inner product operation of linear algebra. For a pair of lists
A
and
B, the result is the application of the reductive operation
f
to the result of applying the binary pervasive operation
g
to
A
and
B. It is assumed that
f
is one of the seven reductive operations of Nial:
sum, product, or, and, max, min
, or
link
and that
g
distributes pairwise. For tables,
OUTER g
is applied to the rows of
A
and the columns of
B
and
f
is applied to each item of the outer product.
Thus,
INNER [+,*]
is equivalent to matrix multiplication in linear algebra and
INNER [or,and]
is boolean matrix product. For higher dimensional arrays, the lists are formed by "pushing down" the last axis of
A
and the first axis of
B.
2 3 4 INNER [+,*] 4 5 6
47
loll INNER [or,and] olol
l
A := 2 4 reshape count 8;
B := 4 3 reshape tell 8;
A B
+-------+-----+
|1 2 3 4|0 1 2|
|5 6 7 8|3 4 5|
| |6 7 0|
| |1 2 3|
+-------+-----+
A INNER [sum,times] B
28 38 24
68 94 64
Definition
INNER IS TRANSFORMER f g (OPERATION A B) {
rows A OUTER (f g) (0 split B) }
Equation
shape (A INNER [f,g] B) = (front shape A) link (rest shape B)
The operation
innerproduct
computes the mathematical inner product of real vectors and matrices using special code for efficiency. For real matrices, it produces the same result as
A INNER [+,*] B, but computes the result more rapidly for large arguments. It coerces boolean and integer arrays to reals. The name
ip
is provided as an abbreviation.
A := 2 4 reshape count 8;
B := 4 3 reshape tell 8;
A B
+-------+-----+
|1 2 3 4|0 1 2|
|5 6 7 8|3 4 5|
| |6 7 0|
| |1 2 3|
+-------+-----+
A innerproduct B
28. 38. 24.
68. 94. 64.
The number of columns of
A
must match the number of rows in
B.
An
interrupt
is an event that causes the operating system to suspend its operation and address a requirement of higher priority. Typically, interrupts occur to handle input/output. However, an interrupt also occurs when a fault is detected.
In the default mode of operation of Q’Nial, most fault values are not created. Rather, an interrupt is triggered. A description of the fault triggering mechanism is given under
fault triggering.
The user can interrupt execution by pressing
<Ctrl-c> <Return>
at the keyboard in console versions, or clicking on the
STOP
button in the GUI version. This capability can be turned off using
setinterrrupts. An interrupt can also be triggered under program control using the expression
Toplevel.
The operation
inverse
computes the mathematical inverse of a square matrix
A
returning a square matrix of the same shape. If
A
is
singular
within numerical limits, the result is the fault
?singular. The name
inv
is provided as an abbreviation.
seed 0.5;
A := ceiling ( 100. * (3 3 reshape random 9))
50 44 13
78 74 90
98 67 17
inverse A
-0.0736442 0.00189821 0.0462669
0.115652 -0.00654341 -0.053798
-0.0312664 0.0148461 0.00413593
A innerproduct inverse A
1. -6.93889e-18 -7.11237e-17
9.4369e-16 1. -1.97758e-16
3.60822e-16 -6.93889e-18 1.
The final computation shows that the result is not always an exact inverse due to roundoff errors introduced by using floating point arithmetic.
Equations
A a square matrix ==> A innerproduct (inverse A) innerproduct A = A (within roundoff error)
inverse A innerproduct B = A solve B (within roundoff error)
An array
A
is said to be an
item
of array
B
if
B
holds
A
at one or more locations. The term is a relative one; we cannot speak of item except in reference to the array that holds it. The items of an array A are the objects at the locations at the top level.
The number of items in an array is called the tally of the array. Because an array is rectangular, the tally is the product of the shape.
The following names are give to common array structures:
# of items
# of axes
Name
0
1
empty list
1
0
single
1
1
solitary
1
2
1 by 1 table
2
1
pair
3
1
triple
4
1
quadruple
The arrays of Nial are a recursive data type. That is, the items of an array are also arrays. Since an array has arrays as items, it may contain data at lower levels than the top one. A
path
is a list of addresses that describes a data object at some depth within the array.
An array is said to be
simple
if all its items are atomic.
A
part
of an array is a data object that is contained at some level within the array. The atomic parts of an array are called the
leaves
of the array. The simple parts are called
twigs
. The term
level
is used informally to describe the relative position of a part within the nesting structure of an array. An item is at the first or top level, an item of an item is at the second level, etc.
The transformer
ITERATE
is used to apply the operation
f
sequentially to the items of
A
in row major order. (Row major order means across the rows moving left to right, starting at the top row and then going down the rows. An example is given under list.) The result is the result of applying
f
to the final item. If
A
has no items, the result is the fault
?noexpr.
ITERATE write "Hello "out "there.
Hello
out
there.
 The example shows
ITERATE
being used to apply
accum
to the list 3 4 5, which results in
A
having the values 23, 27 and 32.
With
ITERATE, an operation that has a side effect can be applied to an array of arguments sequentially in a specific sequence. On the other hand, the order of application is undefined for
EACH.
A second major difference between
EACH
and
ITERATE
is that
ITERATE
returns only the result of the last application whereas
EACH
returns the array of all the results.
 There are no syntactic restrictions as to whether or not a particular operation may be applied in infix or prefix form. A fault is returned at run time if an operation is used inappropriately.
Summary of Juxtapositional Syntax
The following table illustrates the uses of juxtaposition in Nial, where
A
and
B
are array-expressions,
f
and
g
are operation-expressions, and
T
is a transformer:
laminate IS OPERATION I A {
IF equal EACH shape A THEN
Axesofitems := axes first A;
link (I take Axesofitems) (I drop Axesofitems + 1) blend A
ELSE
fault '?conform error in laminate'
ENDIF }
The operation
last
returns the last of the items of
A. If
A
is empty, it returns the fault
?address. The operation
last
is a special case of
pick
(because
first
is defined in terms of
pick) and its behaviour is determined by that of
pick.
Latent
is used in closed applications so that an application can be started when the workspace is loaded.
Latent
can establish any default or initial conditions desired.
Latent IS {
settrigger o;
set "log;
StartApp;
Bye; }
In the example,
Latent
is defined to turn of triggering of faults, to turn on session logging, to start the application and then to terminate the session.
The transformer
LEAF
modifies an operation
f
into an operation that applies f to every atom of the argument
A. The result of applying
LEAF f
to
A
has the same shape as
A. If
f
maps atoms to atoms, the result has the same structure as
A.
The first example shows that the result of applying a
LEAF
transform to a table is a table of the same shape. The atoms have been mapped to 1 since the tally of an atom is 1. The second example shows that applying a
LEAF
transform to a list gives a list of the same length. However, the structure of the result is not preserved in this example because
tell
maps an integer to a list of integers.
 An item is at the first or top level, an item of an item is at the second level, etc.
An atom is viewed in two ways. As an indivisible data object it is viewed as having no levels and cannot be broken into subarrays. As an array data structure it is viewed as a single holding itself and therefore has an infinity of levels. This view is necessary for atomic arrays to fit the theory of nested array mathematics.
The number of levels to reach an atom along each path need not be the same. For example, in the following array, the phrase "hello is at the first level, the integer 23 is at the second level and the character `b is at the third level.
Some of the operations of Nial that operate on simple arrays are extended to arbitrarily nested arrays by being applied to the atoms at the deepest level. These are called
pervasive
operations.
The variable
Libpath
is used by the operation
library
as a list of paths to directories that are to be checked for the definition file named in the
library
argument. The directories defined by
Libpath
are searched before the system dependent library directories.
The operation
library
loads a definition from the Q’Nial library of definition files.
Nm
is the name of a library file as a phrase or string. The name is augmented with the path information for the library and loaded into the workspace using
loaddefs.
Sw, an optional argument, is either 0 (the default) or 1. If
Sw
is 0, there is no display of the file as it is loaded. If
Sw
is 1, the file is displayed as the file is read.
Some of the programs in the library are grouped in definition files by function. When such a file is specified, all the definitions in the file are loaded. If it is desired to load only one operation from a definition file which contains several operations, it is necessary to edit the library file in order to isolate the desired operation in one file.
library "labeltable
Library
searches for files using paths that are provided in its definition. Before searching for system defined directories, it searches in the directories named in the global variable
Libpath, which is empty by default.
To modify Libpath, either edit the standard definitions file
defs.ndf, or assign it a value dynamically.
The operation
like
compares two arrays
A
and
B
and returns
true
if all the items of
A
are items of
B
and vice versa. Otherwise it returns
false. The operation
like
corresponds to set equality at the first level of nesting.
2 3 like 3 2 2 3 2
l
2 3 like 3 2 4
o
'hello world' like 'whole door'
l
Definition
like IS OPERATION A B { (A allin B) and (B allin A)}
The operation
link
returns the list of the items of the items of its argument. If it is applied to a pair of arrays
A
and
B, the result is the items of
A
followed by the items of
B.
If link is applied to an arbitrary array
A, the items of the first item of
A
are followed by the items of the second item of
A, etc.
If
A
is empty, the result is
Null. If
A
is not empty but there are no items in all the items of
A, the result is also
Null.
The first example shows that the link of two lists of length 6 and 4 is a list of length 10. The second example illustrates the linking of three strings.
Link
is similar to a set-union operation, although it does not remove duplicates in the representation. The composition of
cull
with
link
may be more appropriate as a union operation.
Equations
link A = link list A
link A = list link A
link A = EACH list link A
tally link A = sum EACH tally A
link solitary A = list A
link Null = Null
link EACH link A = link link A
cart link A = EACH link cart EACH cart A
EACH f link A = link EACH EACH f A
simple A ==> link A = list A
and EACH simple A ==> content A = link A
The operation
load
is used to retrieve the saved workspace named by the phrase or string
Wsname. The convention in Q’Nial is to save workspaces with a file name extension of the form
.nws
to make it easy to identify workspace files in the file system. When saving or loading a workspace, the extension may be omitted.
The effect of loading a workspace is to replace the current workspace with the saved one. To keep the contents of the current workspace,
save
should be used prior to doing a
load.
The operation
load
may be used within a defined expression or operation. However, in such a use, it interrupts the execution of the operation or expression and does the load as though the operation were entered at top level.
If a workspace contains an expression with the name
Latent, load
executes
Latent
when the load is complete. This mechanism can be used to have the load of a workspace automatically begin an application or to chain execution of workspaces.
There is no mechanism to obtain individual objects saved in a workspace.
The operation
loaddefs
is used to load Nial actions (definitions or expressions) from the file named by the phrase or string
Deffilename. The convention in Q’Nial is to name Nial definition files with a file name extension of the form
.ndf
to make it easy to identify such files in the file system.
When loading a file with
loaddefs, the file extension may be omitted. The operation may have an optional second argument to specify the mode of loading. If mode is 0 or if it is omitted, the actions are not displayed as they are processed. If it is 1, the actions are displayed.
The file of actions consists of groups of lines that are treated as a unit. These lines are separated by a blank line. If the first character in the first line of the group is
Several lines, up to a blank line, are treated as one long line. No blank spaces are inserted between the end of one line and the beginning of another. Thus, care should be taken to ensure that identifiers are not joined together.
If a syntax error is detected when a definition is being loaded, the definition is not installed. The absence of the erroneous definition may cause subsequent definitions in the file to fail also. If any errors are detected, the number of errors is displayed when
loaddefs
ends. Only the first syntax error found in each action is reported.
The backslash character,
\, which is used to continue a line of a definition in immediate mode, must not be inserted at the end of lines in a definition file.
The tab characters, if they exist in a definition file, are treated as though they are space characters. Since some editor programs insert tabs automatically, the display of the file on input may be different from the display using the editor.
A definition file may contain uses of
loaddefs. For a large application with many definition files, it is a good idea to have one file which can construct the application by using
loaddefs
to bring in the other files in an appropriate order.
Loaddefs
can also be used to execute a script of Nial expressions. This feature is convenient when simulating a Q’Nial session and capturing its output in a log file.
A
local environment
is a collection of associations that are known within a limited section of program text. These limited sections are formed by blocks, operation-forms and transformer-forms as discussed in the relevant sections below. A name that has a local association in one of these forms is said to have local scope.
Program fragments in which local variables are being assigned can be nested, so that one local scope surrounds another. A local association is not visible outside the construct in which it is defined; and a name with local scope can hide associations that the name has in surrounding scopes.
At any point in a program fragment, there is a current environment consisting of all names whose associations are visible. It includes the names having local scope in the program fragment being executed, names that are visible in the surrounding scopes and names that have global scope.
In program text, the scope of all names is determined by the static structure of the program text. The one exception is text that has the operation execute applied to it under program control.
In a local environment, a variable identifier can be chosen the same as a predefined or user-defined global definition name. Such a choice makes the global use of the name unavailable in the local context.
In any context, an identifier can name only one of: a variable, an array- expression, an operation-expression, or a transformer-expression. During one session, the role of a name, i.e. the class of syntactic object it names, cannot be changed.
If a block is used as a primary-expression, the local environment created by a block is determined by the block itself. If it is the body of an operation-form, the local environment includes the formal parameter names of the operation-form as variables.
A block delimits a local environment. It allows new uses of names which do not interfere with uses of those names outside the block. For example, within a block, a predefined operation name can be redefined and used for a different purpose. Only the reserved words of Q’Nial cannot be reused in this fashion. Definitions that appear within the block have local scope. That is, the definitions can be referenced only in the body of the block. Variables assigned within the block may or may not have local scope, depending on the appearance of a local and/or a nonlocal declaration. If there is no declaration, all assigned variables have local scope. Declaring some variables as local does not change the effect on undeclared variables that are used on the left of assignment. They are automatically localized.
If a nonlocal declaration is used, an assigned name that is on the nonlocal list is sought in surrounding scopes. If the name is not found, a variable is created in the global environment.
During the parse of the assign-expression appearing in a block, each name on the variable list is sought in the local environment. If the name exists in the local environment, the assignment affects the local association. If a name does not exist in the local environment and no reference has been made to a nonlocal variable with the same name, a local variable is created in the block. An assign-expression parsed in the global environment creates a global variable if a variable with that name does not already exist.
An operation-form defines a local environment. The formal parameter names are names of local variables. If the body of the operation form is a block, the local environment of the block is extended to include the formal parameters. When the operation is applied, the formal parameter names are assigned from the value of the actual argument. If there is only one formal parameter, the actual argument is assigned to it as a whole; otherwise, the items of the actual argument are assigned to the formal parameters in corresponding order. If there is a length mismatch between the list of formal parameter names and the values of the actual argument, the fault
?op_parameter
is returned.
The value of the application of the operation is the value of the body of the operation-form, which is evaluated with the local variables in the parameter list assigned as described above. In determining the association for a name that appears in the body of an operation form, Q’Nial looks for the name in the local environment. If the name is not found locally, the name is sought in surrounding environments until it is found or until the global environment is searched. If it is not found, a fault
?unknown identifier:
is given when the operation-form is analyzed (parsed).
Operation-forms are most frequently used in definitions where they are given an associated name. However, an operation-form can appear directly in an expression provided it is enclosed in parentheses. In this usage, it can be an argument to a transformer name or can be applied to an array argument.
The operation
execute
can be used within the execution of a block to make an assignment to variables or to invoke the definition mechanism. If
execute
is used to make a new definition or to create a new variable, the resulting variable or definition is placed in the global environment. However, if the block has local variables or local definitions, execute can be used to change a local version dynamically. A similar situation occurs with dynamic alteration of variables using
assign.
Q’Nial provides a facility to record the actions in a session in a text file. The default name for the log file is
auto.nlg. Logging is initiated by:
set "log
nolog
Logging is ended by:
set "nolog
log
The log file name can be changed using:
setlogname "newname
auto.nlg
A log file is opened and closed on each usage by the internal logging routine. As a result, the log file is always available if the session is terminated unexpectedly. If a file with the name of the log file exists when
set "log
is executed, the logging information is appended at the end.
The operation
lower
is used to partition an array
A
along its axes by indicating that the last
N
axes are to become axes of the items of the result. The remaining axes become the axes of the result. N must be an integer in the range from 0 to
valence A. The result is an array of shape given by dropping the last
N
items of
shape A. The items of the result have the shape given by taking the
last N
items of
shape A. Thus, the
1 lower
of an array of shape 3 4 2 is a 3 by 4 table of pairs. The
2 lower
of the same array is a triple of 4 by 2 tables.
 Symbol
<
is a synonym for
lt.
The atoms in Nial are organized as a lattice using
<=
for the ordering. The numeric atoms are comparable across types but literal atoms are not comparable with numeric atoms. The literal types are not comparable across types. A comparison between incomparable objects results in
false.
 The last two examples show the difference between comparing two strings, where the operation is distributed by its pervasive property; and comparing the corresponding phrases.
Definition
lt IS OPERATION A B ( ( A lte B ) and not ( A mate B ) )
The operation
lte
compares two atoms
A
and
B
with the
less than or equal
relation, returning
true
if
A
is less than
B
and
false
otherwise. The symbol
<=
is a synonym for
lte.
The atoms in Nial are organized as a lattice using
<=
for the ordering. The numeric atoms are comparable across types but numeric and literal atoms are incomparable. The literal types are not comparable across types. A comparison between incomparable objects results in
false.
 The last two examples show the difference between comparing two strings, where the operation is distributed by its pervasive property; and comparing the corresponding phrases.
The operation
match
compares two atoms
A
and
B
for exact equality, returning
true
if
A
is equal to
B
and
false
otherwise.
Match
cannot be used to determine the equality of atoms of different numeric or literal type, as when comparing the real number 3.0 with the integer 3.
3 match 3
l
3.0 match 3
o
"o match `o
o
1.0 match l 1 1.0
ool
` match 'a list of letters'
olooooloolooooooo
1 match tell 2 3
+--+--+--+
|oo|ol|oo|
+--+--+--+
|lo|ll|lo|
+--+--+--+
The second last example shows that the binary pervasive extension of
match
allows a lists of atoms to be compared for a value, returning a bitstring of results. The last example shows where in the structure of tell 2 3 a 1 is held.
The operation
mate
compares two atoms
A
and
B
for equality with type coercion, returning
true
if
A
is equal to or can be coerced to
B
as a number; and
false
otherwise.
Mate
is used to determine the equality of atoms of different numeric type, as when comparing the real number 3.0 with the integer 3. It cannot be used to determine equality of literal types.
3 mate 3
l
3.0 mate 3
l
"o mate `o
o
1.0 mate l 1 1.0
lll
The last example shows that the binary pervasive extension of
mate
allows a lists of atoms to be compared for a value, returning a bitstring of results.
Definition
mate IS OPERATION A B { (A lte B) and (B lte A) }
The operation
max, when applied to a simple array
A, finds the least atom that is greater than or equal to all the items. If the atoms of
A
are all positive numbers, for example,
max A
returns the biggest. If the items are not comparable, the result is the fault
?I, the atom in the lattice of Nial atoms that is greater than or equal to all other atoms (the zenith). If A is empty, the result is the fault
?O, the atom that is less than or equal to all other atoms (the nadir).
Max
is extended to arbitrary arrays by its multi pervasive behaviour. It is a reductive operation in that it reduces a simple array to a single atom. Applied to a pair of simple arrays, it produces a simple array with the corresponding items compared.
If the items of
A
are all numeric type, they are comparable. The result is the highest numeric type represented in the array, where boolean is the lowest numeric type and real number is the highest.
max 3 45 23 18 3.5
45.
max 3 "abc
?I
max "abc "def "c
def
max 'apples' 'orange'
orpngs
The first example shows that
max
of a list of numbers of different type is the maximum number, coerced to the highest type. The second shows that the integer 3 and the phrase "abc" are incomparable and the result is the fault ?I. The third example shows that phrases are directly comparable. The last shows that strings are compared on a character by character basis.
Equations
A max B = B max A
max EACH max A =f= max link A
atomic A ==> max B lte A = and (B EACHLEFT lte A)
max Null = ??O
The operation
min, when applied to a simple array
A, finds the greatest atom that is less than or equal to all the items. If the atoms of
A
are all positive numbers, for example,
min A
returns the smallest number. If the items are not comparable, the result is the fault
?O, the atom in the lattice of Nial atoms that is less than or equal to all other atoms (the nadir). If
A
is empty, the result is the fault
?I, the atom that is greater than or equal to all other atoms (the zenith).
Min
is extended to arbitrary arrays by its multi pervasive behaviour. It is a reductive operation in that it reduces a simple array to a single atom. Applied to a pair of simple arrays, it produces a simple array with the corresponding items compared.
If the items of
A
are all numeric type, they are comparable. The result is the highest numeric type represented in the array, where boolean is the lowest numeric type and real number is the highest.
min 3 45 23 18 3.5
3.
min 3 "abc
?O
min "abc "def "c
abc
min 'apples' 'orange'
apalee
The first example shows that
min
of a list of numbers of different type is the minimum number, coerced to the highest type. The second shows that the integer 3 and the phrase "abc" are incomparable and the result is the fault
?I. The third example shows that phrases are directly comparable. The last shows that strings are compared on a character by character basis.
Equations
A min B = B min A
min EACH min A = min link A
atomic A ==> A lte min B = and (A EACHRIGHT lte B)
min Null = ??I
The operation
minus
returns the result of subtracting two numeric atoms. It coerces the type of the atoms to be the higher type, if they differ; or to integer, if both are boolean. The result is of the type of the coerced arguments.
The symbol
-
is a synonym for
minus. Care must be taken when using this symbol because it also is used to form negative constants and the latter use has higher priority. Thus,
A := 10;
A - 1
9
does a subtraction, but
A -1
10 -1
forms a pair.
If one argument is numeric and the other is a fault or if both arguments are the same fault, the answer is the fault. In all other cases when one or more of the arguments is not numeric, the result is the arithmetic fault
?A.
R gets l 2 2.5 `a "abc ??error
l 2 2.5 a abc ?error
R OUTER minus R
0 -1 -1.5 ?A ?A ?error
1 0 -0.5 ?A ?A ?error
1.5 0.5 0. ?A ?A ?error
?A ?A ?A ?A ?A ?A
?A ?A ?A ?A ?A ?A
?error ?error ?error ?A ?A ?error
The above example illustrates all combinations of atom types for the two arguments to
minus.
Equations
0 minus A = opposite A
A minus B = A plus opposite B
The operation
mix
is intended to be used on an array
A
with items which are all the same shape. In this case, the shape of the result is formed by the shape of
A
linked with the shape of the items. The list of items of the result is the link of the items of
A. If
A
does not have equally shaped items, the fault
?conform
is returned.
The first example shows that the result of mixing a triple of pairs is a 3 by 2 table. The second example shows that mixing a pair of 2 by 3 tables is a 2 by 2 by 3 array, with the tables as the planes of the array.
The operation
mix
is the left inverse of the operations
rows
and
raise
for nonempty arrays. It is often used to transform from a
list of lists
view of data to a table view of data. In practice, transformations between the two representations on large arrays should be avoided in Q’Nial since considerable work is involved in doing the restructuring.
Definition
mix IS OPERATION A {
IF empty A THEN
shape A append 0 reshape A
ELSEIF not equal EACH shape A THEN
??conform
ELSE
shape A link shape first A reshape link A
ENDIF }
Equations
not empty A and equal EACH shape A ==> shape mix A = link (shape A)(shape first A)
equal EACH shape A ==> list mix A = link A
not empty A ==> mix rows A = A
I in (axes A + 1) and not empty A ==> mix (I raise A) = A
The operation
mod
returns the remainder of dividing integer
A
by integer
B. If the divisor
B
is zero, the result is
A. If it is negative, the result is the fault
?negative divisor.
The operation
mod
coerces a boolean argument to an integer. It produces a fault if either argument is not an integer or a boolean.
R gets l 2 2.5 `a "abc ??error
l 2 2.5 a abc ?error
R OUTER mod R
0 1 ?A ?A ?A ?error
0 0 ?A ?A ?A ?error
?A ?A ?A ?A ?A ?error
?A ?A ?A ?A ?A ?A
?A ?A ?A ?A ?A ?A
?error ?error ?error ?A ?A ?error
(5 mod 3)(-5 mod 3)(5 mod -3)(5 mod 0)
2 1 ?negative divisor 5
The first example illustrates all combinations of atom types for the two arguments to
mod.
The remainder on division by a positive integer
B
is always a number between 0 and
B - 1. If
A
is negative, the integer returned obeys the rule of modular arithmetic as described in the definition. The result of
A mod 0
is
A
rather than a fault, since that is the preferred result in mathematics.
Definition
mod IS OPERATION A B { floor (A - (B * (A quotient B)) ) }
The operation
mold
is provided for backward compatibility with earlier versions of Nial. The current definition is a synonym for
take. The previous definition was a reshaping operation that was almost always used on lists and
take
has the same effect as the previous definition on lists.
Each operation
f
in this class reduces a simple array (an array of atoms) to an atom.
A multi pervasive operation maps an array having items of identical structure to one with the same structure, applying the operation to the simple arrays formed from all the atoms in corresponding positions in the atoms.
There are six operations in this class. They are the
reductive
operations of arithmetic and logic.
If a multi pervasive operation is applied to an array that does not have items of the same shape, the effect is to build a conformable pair by replicating an atom or solitary item of the array to the common shape of the other items. If two or more of the items have tally different from one and are of unequal shape, the fault
?conform
is returned. On a pair, a multi pervasive operation behaves the same way as a binary pervasive operation does.
sum [3 4 5 6 , 5 , 7 8 9 10] = 15 17 19 21
l
The following table describes the multi pervasive operations.
 Its name is a local definition name. If the name is also used outside the block, the external meaning is not known in the block.
Nested definitions can be used to encapsulate support definitions within a larger definition that is to be made available to other users. This avoids cluttering up the name space with names that might interfere with the user's other work. It is often easier to develop the large definition without encapsulation and package it in encapsulated form once the design is completed.
The command
next
is used in debugging a definition that has been suspended using Break or <Ctrl B>. The effect of
next
is to execute the next expression in an expression sequence and to suspend execution again. If the current expression involves a call on a defined operation or expression, execution of the entire definition is completed and execution is suspended on the expression after the current one. If the current expression is the last one in the expression sequence where the break began, the effect is the same as using
resume.
The related command
nextv
displays the result of the expression executed on a step before displaying the next expression.
The expression
Nialroot
returns a string giving the path to the directory holding the help directory, the initial directory and the library directories. Nialroot is defined before Q’Nial is run. In Windows and Unix versions this is set up as part of the installation procedure.
The expression No_expr returns the fault
?missing_expr. It is the default value for an expression name declared to be
external
or that has been erased.
The operation
no_op
returns the fault
?missing_op
when applied to any array
A. It is the default value for an operation name declared to be external or that has been erased.
The transformer
NO_TR
transforms any operation
f
to an operation that returns the fault
?missing_tr
when applied to any array
A. It is the default value for a transformer name declared to be external or that has been erased.
The operation
not
reverses the value of a boolean. It returns
true
if
A
is
false
and
false
if
A
is
true. If
A
is any other atom, the result is the logical fault
?L.
not l
o
not lollo
olool
not 2 "abc o
?L ?L l
Equations
not not not A = not A
not and A =f= or EACH not A
not or A =f= and EACH not A
The expression
Null
denotes the empty list. It is the array returned as the shape of a single and is equal to the empty string. The empty list notation
[ ]
also denotes the empty list
Null.
Null = shape 5
l
Null = []
l
Null = ''
l
Since there is an empty array for each shape containing a zero, unless you are certain
A
is a list, the test:
A = Null
should not be used in place of
empty A.
set "sketch; Null
set "diagram; Null
+
|
+
Null
is not displayed when sketch display mode is set. In diagram mode it is displayed as the left border of a list diagram.
A selection operation such as
pick
or
first
applied to
Null
results in the fault
?address.
Definition
Null IS shape 0
Equations
shape single A = Null
shape Null = 0
atomic A ==> Null = shape A
The operation
numeric
tests whether or not
A
is a numeric atom. It returns
true
if
A
is a boolean atom, an integer atom or a real atom,
false
otherwise.
numeric 3.5
l
numeric l 45 3.78
o
numeric Null
o
Definition
numeric IS OPERATION A { isboolean A or isinteger A or isreal A }