1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
_T_h_e_ _%_f_r_e_e_ _d_i_r_e_c_t_i_v_e_ _a_n_d_ _p_a_r_a_m_e_t_r_i_s_e_d_ _s_c_r_i_p_t_s
It is permitted to construct a script containing definitions which are
dependent on information which will be supplied only when the script is
made the subject of a %_i_n_c_l_u_d_e directive. Such a script is said to be
_p_a_r_a_m_e_t_r_i_s_e_d. This is indicated by the presence in the script of a
directive of the form
%_f_r_e_e { signature }
where `signature' is a list of specifications of the identifiers for
which bindings will be provided at %_i_n_c_l_u_d_e time. A script may contain
at most one %_f_r_e_e directive, which must therefore give all the
identifiers on which the script is parametrised. The %_f_r_e_e directive
may appear anywhere in the script, but for clarity it is recommended
that you place it at or near the top.
For example a script (called "matrices" say) defining the notion of
matrix sum and matrix product, for matrices of as-yet-unspecified
element type, could be written as follows:-
%_e_x_p_o_r_t matmult matadd
%_f_r_e_e { elem :: _t_y_p_e
zero :: elem
mult, add :: elem->elem->elem
}
matrix == [[elem]]
matadd :: matrix->matrix->matrix
matadd xx yy = [[add a b|(a,b)<-zip2 x y]|(x,y)<-zip2 xx yy]
matmult :: matrix->matrix->matrix
matmult xx yy = outerprod innerprod xx (transpose yy)
innerprod x y = sum [mult a b|(a,b)<-zip2 x y]
_w_h_e_r_e
sum = foldr add zero
outerprod f xx yy = [[f x y|y<-yy]|x<-xx]
Note that the identifiers declared under %_f_r_e_e may denote types as well
as values. When we write a %_i_n_c_l_u_d_e directive for the above script we
must provide bindings for all of its free identifiers. The bindings are
given in braces following the pathname (and before the aliases, if any).
Thus:-
%_i_n_c_l_u_d_e "matrices" {elem==num; zero=0; mult=*; add=+; }
In the scope of the script containing the above directive the
identifiers `matmult' and `addmult' will be available at type
[[num]]->[[num]]->[[num]] and will behave as if their definitions had
been written using 0, (+), (*) in place of the identifiers zero, add,
mult.
The order in which the bindings are given is immaterial (it need not be
the order in which the identifiers occurred in the %_f_r_e_e directive) but
a binding must be given for each free identifier of the %included
script. Note that the binding for a type is given using `==' and for a
value using `='. If the types of all the bindings (taken together) are
not consistent with the information given in the free directive of the
%included script, or if any required binding is missing, the compiler
will reject the %_i_n_c_l_u_d_e directive as incorrect.
The main advantage of a parametrised script is that different bindings
may be given for its free identifiers on different occasions. For
example the same script "matrices" may be invoked with different
bindings to provide a definition of matrix addition and multiplication
over matrices with elements of type bool. Thus:-
%_i_n_c_l_u_d_e "matrices" {elem==bool; zero=False; mult=&; add=\/; }
It is even possible to %_i_n_c_l_u_d_e the same parametrised script twice in
the same scope (presumably with different bindings for the free
identifiers) but in this case it will be be necessary to alias apart the
two sets of exported identifiers to avoid a nameclash. So we might add
`b_matadd/matadd b_matmult/matmult' to the above directive if it were
being used in the same script as the previous one.
_M_i_s_c_e_l_l_a_n_e_o_u_s_ _p_o_i_n_t_s
By default the identifiers declared %_f_r_e_e in a parametrised script are
not exported from the script. As always this can be overridden by
explicitly listing them in an %_e_x_p_o_r_t directive.
Free typenames of non-zero arity are declared in the following style.
%_f_r_e_e { stack * :: _t_y_p_e
table * ** :: _t_y_p_e
...
}
The corresponding bindings could be as follows
%_i_n_c_l_u_d_e ... {stack * == [*]; table * ** == [(*,**)]; ... }
When a parametrised script exports a locally created typename (other
than a synonym type), each instantiation of the script by a %_i_n_c_l_u_d_e is
deemed to create a NEW type (this is relevant to deciding whether or not
two types are the same for the purpose of readopting a type orphan, see
previous manual section). This is because the compiler assumes that an
abstract or algebraic type defined in a parametrised script will in
general have an internal structure that depends on the free identifiers.
Finally note that the bindings for the free identifiers of a
parametrised script must always be given EXPLICITLY. For example
suppose we wish to %_i_n_c_l_u_d_e the file "matrices" in a script already
containing a type called `elem' over which we intend to do matrix
multiplication. We must write
%_i_n_c_l_u_d_e "matrices" {elem==elem; etc. }
The binding `elem==elem' is not redundant, nor is it cyclic, because the
two `elem's involved refer to two different scopes (on the left of the
binding, that of the includee, and on the right that of the script
containing the directive).
|