1 | \chapter{Future Work}
|
---|
2 |
|
---|
3 | \vspace*{-18pt}
|
---|
4 | The following are feature requests related to type-system enhancements that have surfaced during the development of the \CFA language and library, but have not been implemented yet.
|
---|
5 | Currently, developers must work around these missing features, sometimes resulting in inefficiency.
|
---|
6 |
|
---|
7 |
|
---|
8 | \section{Closed trait types}
|
---|
9 |
|
---|
10 | Currently, \CFA does not have any closed types, as open type are the basis of its unique type-system, allowing new functions to be added at any time to override existing ones for trait satisfaction.
|
---|
11 | Locally-declared nested-functions,\footnote{
|
---|
12 | Nested functions are not a feature in C but supported by \lstinline{gcc} for multiple decades and are used heavily in \CFA.}
|
---|
13 | or a function-pointer variable can be used to make a type satisfy a certain trait temporarily in a limited lexical scope.
|
---|
14 | However, the lack of closed types in such a \emph{duck typing} scheme presents two problems.
|
---|
15 | \begin{enumerate}[leftmargin=*]
|
---|
16 | \item
|
---|
17 | Library implementers normally do not want users to override certain operations and cause the behaviour of polymorphic invocations to change.
|
---|
18 | \item
|
---|
19 | Caching and reusing resolution results in the compiler is effected, as newly introduced declarations can participate in assertion resolution;
|
---|
20 | as a result, previously invalid subexpressions suddenly become valid, or alternatively cause ambiguity in assertions.
|
---|
21 | \end{enumerate}
|
---|
22 | Sometimes those interfaces are fairly complicated, \eg the \CFA I/O library traits @istream@ and @ostream@ each have over 20 operations.
|
---|
23 | Without the ability to store and reuse assertion resolution results, each time the compiler encounters an I/O operation (denoted by the pipe operator @?|?@ like the @<<@, @>>@ stream operators in \CC) it has to resolve the same set of assertions again, causing a lot of repetitive work.
|
---|
24 | Previous experiments have shown that the I/O assertions often account for over half of the number of assertions resolved in a \CFA translation unit.
|
---|
25 | Hence, eliminating repetitive assertion resolution, which are very unlikely to change by new overloads, can provide a significant performance benefit.
|
---|
26 |
|
---|
27 | \VRef[Figure]{f:basicOstreamTrait} shows the basic output-stream trait in \CFA and the overloaded I/O operator is declared as:
|
---|
28 | \begin{cfa}
|
---|
29 | forall( ostype & | basic_ostream( ostype ) ) ostype & ?|?( ostype &, int ); // one for each basic type
|
---|
30 | \end{cfa}
|
---|
31 | The \CFA polymorphic calling convention means all of these trait functions are repeatedly resolved at each output-operator call-site and then become implicit arguments of the call.
|
---|
32 | This creates both compile-time and run-time overheads.
|
---|
33 | As stated, resolving assertions takes the most amount of time during compilation, and it is common to see in the current codebase and test suite that the majority of compilation time is spent in I/O statements.
|
---|
34 | With closed types, the I/O assertions can be resolved once and this information reused everywhere.
|
---|
35 | Furthermore, closed types can have a different calling convention of pushing a virtual-table pointer rather than individual arguments onto the stack.
|
---|
36 | Although for I/O, this benefit might not be significant, since these operations involve system calls that are inherently slow.
|
---|
37 |
|
---|
38 | \begin{figure}
|
---|
39 | \begin{cfa}[escapechar={\#}]
|
---|
40 | forall( ostype & ) @trait@ basic_ostream {
|
---|
41 | // private
|
---|
42 | bool sepPrt$( ostype & ); #\C[3.35in]{// get separator state (on/off)}#
|
---|
43 | void sepReset$( ostype & ); #\C{// set separator state to default state}#
|
---|
44 | void sepReset$( ostype &, bool ); #\C{// set separator and default state}#
|
---|
45 | const char * sepGetCur$( ostype & ); #\C{// get current separator string}#
|
---|
46 | void sepSetCur$( ostype &, const char [] ); #\C{// set current separator string}#
|
---|
47 | bool getNL$( ostype & ); #\C{// get newline}#
|
---|
48 | bool setNL$( ostype &, bool ); #\C{// set newline}#
|
---|
49 | bool getANL$( ostype & ); #\C{// get auto newline (on/off)}#
|
---|
50 | bool setANL$( ostype &, bool ); #\C{// set auto newline (on/off)}#
|
---|
51 | bool getPrt$( ostype & ); #\C{// get fmt called in output cascade}#
|
---|
52 | bool setPrt$( ostype &, bool ); #\C{// set fmt called in output cascade}#
|
---|
53 | // public
|
---|
54 | void nlOn( ostype & ); #\C{// turn auto-newline state on}#
|
---|
55 | void nlOff( ostype & ); #\C{// turn auto-newline state off}#
|
---|
56 | void sep( ostype & ); #\C{// turn separator state on}#
|
---|
57 | void nosep( ostype & ); #\C{// turn separator state off}#
|
---|
58 | bool sepOn( ostype & ); #\C{// set default state to on}#
|
---|
59 | bool sepOff( ostype & ); #\C{// set default state to off}#
|
---|
60 | const char * sepGet( ostype & ); #\C{// get separator string}#
|
---|
61 | void sepSet( ostype &, const char [] ); #\C{// set separator to string (15 maximum)}#
|
---|
62 | const char * sepGetTuple( ostype & ); #\C{// get tuple separator string}#
|
---|
63 | void sepSetTuple( ostype &, const char [] );#\C{// set tuple separator to string (15 maximum)}#
|
---|
64 | void ends( ostype & ); #\C{// end of output statement}\CRT#
|
---|
65 | int fmt( ostype &, const char format[], ... ) __attribute__(( format(printf, 2, 3) ));
|
---|
66 | };
|
---|
67 | \end{cfa}
|
---|
68 | \caption{\lstinline{basic_ostream} trait}
|
---|
69 | \label{f:basicOstreamTrait}
|
---|
70 | \end{figure}
|
---|
71 |
|
---|
72 | A \CFA closed trait type is similar to a Haskell type class requiring an explicit instance declaration.
|
---|
73 | The syntax for the closed trait might look like:
|
---|
74 | \begin{cfa}
|
---|
75 | forall( ostype & ) @closed@ trait basic_ostream { /* above declarations */ };
|
---|
76 | @trait@ basic_ostream struct ofstream { ... }; // bind trait to type, implement basic_ostream
|
---|
77 | \end{cfa}
|
---|
78 | At the trait declaration, all the required trait functions must be inferred and specialized in the usual way.
|
---|
79 | The list of inferred and specialized functions is then saved as an array of function pointers (vtable), and polymorphic functions using the closed type are passed the vtable, instead of a list of implicit parameters.
|
---|
80 | \begin{cfa}
|
---|
81 | ostype ?|?( ostype &, int, @basic_ostream_vtable *@ ); // parametric vtable
|
---|
82 | sout | 42; /* becomes */ ?|?( sout, 42, @ofstream_trait@ ); // generated for closed type ofstream
|
---|
83 | \end{cfa}
|
---|
84 |
|
---|
85 | Closed types can provide additional encapsulation.
|
---|
86 | In \VRef[Figure]{f:basicOstreamTrait}, the functions ending with dollar sign @$@ are meant to be private (internal to the library) so client code should not be able to see or call these functions directly.
|
---|
87 | This kind of information hiding is impossible with the current trait and assertion system, because the trait information must be visible at the call site for the polymorphic call to resolve.
|
---|
88 | With closed traits and types, it is possible to forward declare a closed trait (which does not make sense otherwise) and its vtable without exposing any details to the client.
|
---|
89 | That is, all the information necessary to call trait functions is now captured in a single vtable pointer built at compile time and the type of the vtable pointer is opaque.
|
---|
90 |
|
---|
91 |
|
---|
92 | \section{Associated Types}
|
---|
93 |
|
---|
94 | The analysis presented in \VRef{s:AssertionSatisfaction} shows if all type parameters have to be bound before assertion resolution, the complexity of resolving assertions become much lower as every assertion parameter can be resolved independently.
|
---|
95 | That is, by utilizing information from higher up the expression tree for return value overloading, most of the type bindings can be resolved.
|
---|
96 | However, there are scenarios where some intermediate types need to be involved in certain operations, which are neither input nor output types.
|
---|
97 |
|
---|
98 | \CFA standard library provides a few polymorphic container types similar to those found in the \CC standard library.
|
---|
99 | Like \CC, \CFA would like to have a harmonized iterator interface for different container types.
|
---|
100 | While this feature is still under development, the following trait outline is used to present the problem.
|
---|
101 | \begin{cfa}
|
---|
102 | forall( Container, Elem, Iter ) trait iterable {
|
---|
103 | @Iter@ begin( Container );
|
---|
104 | @Iter@ end( Container );
|
---|
105 | @Elem@ & *?( Iter );
|
---|
106 | Iter ++?( Iter );
|
---|
107 | bool ?==?( Iter, Iter );
|
---|
108 | };
|
---|
109 | \end{cfa}
|
---|
110 | These are the \CC operators required for for-loop comprehension, \eg @for (Elem & e : container)@.
|
---|
111 | Note, the iterable trait is meant to describe a property for a container type, but involves two additional types, one for the element type and one for the iterator type.
|
---|
112 | However, @Iter@ is intended to be deduced from the @begin@ and @end@ operations on the container type;
|
---|
113 | the same can be said for the element type @Elem@.
|
---|
114 | The alternative is to make @Elem@ and @Iter@ unbound type-parameters by removing them from the @forall@.
|
---|
115 | As mentioned, unbound type-parameters cause problems, but that is the only way to describe the iterable property.
|
---|
116 |
|
---|
117 | To solve this problem, I propose adding associate-type declarations in addition to the existing unbounded @forall@ type parameter.
|
---|
118 | In the body of a trait declaration, new types can be introduced as the return type of an expression involving type parameters.
|
---|
119 | \begin{cfa}
|
---|
120 | forall( Container ) trait iterable {
|
---|
121 | @type@ Iter begin( Container );
|
---|
122 | Iter end( Container );
|
---|
123 | @type@ Elem & *?( Iter );
|
---|
124 | Iter ++?( Iter );
|
---|
125 | bool ?==?( Iter, Iter );
|
---|
126 | };
|
---|
127 | \end{cfa}
|
---|
128 | This matches conceptually with the iterable trait having only the container type, rather than three types.
|
---|
129 | The iterator type and element type are now viewed as properties of the container type not independent type parameters.
|
---|
130 |
|
---|
131 | There remains one design decision to be made: whether the expression that defines an associated type needs to be uniquely resolved.
|
---|
132 | If the associated type does not appear in the parameter or return types, this requirement seems reasonable.
|
---|
133 | The reason is that the expression cost does not consider any conversions that occur in assertion parameter matching, which essentially means having multiple ways to resolve an associated type always results in ambiguity.
|
---|
134 | The interesting case is when the associated type also appears in the return type, where both resolving the return type overload based on context and resolving the assertion that defines the associate type can help;
|
---|
135 | for the whole expression to resolve, they must agree on a common result.
|
---|
136 |
|
---|
137 | Moss gave the following example to illustrate \CFA assertion system's expressiveness.
|
---|
138 | \begin{cfa}
|
---|
139 | forall( Ptr, Elem ) trait pointer_like {
|
---|
140 | Elem & *?( Ptr ); // Ptr can be dereferenced to Elem
|
---|
141 | };
|
---|
142 | struct list {
|
---|
143 | int value;
|
---|
144 | list * next;
|
---|
145 | };
|
---|
146 | typedef list * list_iterator;
|
---|
147 | int & *?( list_iterator it ) {
|
---|
148 | return it->value;
|
---|
149 | }
|
---|
150 | \end{cfa}
|
---|
151 | This can be viewed as having an associated type, @Elem@, as return, which can potentially vary based on context.
|
---|
152 | Note that the type @list *@ satisfies both @pointer_like( list *, int )@ and @pointer_like( list *,@ @list )@ (the latter by the built-in pointer dereference operator) and the expression @*it@ can be either a @struct list@ or an @int@.
|
---|
153 | Requiring associated types to be unique makes the @pointer_like@ trait not applicable to @list *@, which is undesirable.
|
---|
154 | I have not attempted to implement associated types in \CFA compiler, but based on the above discussions, one option is to make associated type resolution and return type overloading coexist:
|
---|
155 | when the associated type appears in returns, it is deduced from the context and then verify the trait with ordinary assertion resolution;
|
---|
156 | when it does not appear in the returns, the type is required to be uniquely determined by the expression that defines the associated type.
|
---|
157 |
|
---|
158 |
|
---|
159 | \section{User-defined conversions}
|
---|
160 |
|
---|
161 | Missing type-system feature is a scheme for user-defined conversions.
|
---|
162 | Conversion means one type goes through an arbitrary complex process of changing its value to some meaningful value in another type.
|
---|
163 | Because the conversion process can be arbitrarily complex, it requires the power of a function.
|
---|
164 | Arbitrary conversions are already possible through standalone named functions.
|
---|
165 | \begin{cfa}
|
---|
166 | S convert( T ); $\C[2in]{// change T to S}$
|
---|
167 | S s; T t;
|
---|
168 | s = convert( t );
|
---|
169 | \end{cfa}
|
---|
170 | C has implicit/explicit conversions for the builtin types.
|
---|
171 | \begin{cfa}
|
---|
172 | int i = 3.5; $\C{// implicit, change the floating-point bits to integral bits}$
|
---|
173 | int i = (int)3.5 + 17; $\C{// explicit, change the floating-point bits to integral bits}$
|
---|
174 | \end{cfa}
|
---|
175 | When the conversion occurs in expression evaluation can significantly affect the result, where function call has higher precedence than cast.
|
---|
176 | Overloading the cast operator would allow user-defined conversions to look and feel, like regular C casts, as is provided in \CC.
|
---|
177 | \begin{cfa}[escapechar={}]
|
---|
178 | S ?@$@( T ) { /* change T to S */ }
|
---|
179 | s = t;
|
---|
180 | \end{cfa}
|
---|
181 | % $ stop formatting
|
---|
182 | Similarly, constructors can be used for implicit conversion, as in \CC.
|
---|
183 | \begin{cfa}
|
---|
184 | void ?{}( S s, T t ) { ... } $\C{// convert T to S}$
|
---|
185 | s = t; $\C{// rewritten as s = ?{}( s, t )}\CRT$
|
---|
186 | \end{cfa}
|
---|
187 | To integrate properly with the \CFA conversion model, there would need to distinguish between safe and unsafe conversion, and possibly a way to denote conversions as explicit-only or non-chainable, otherwise conversion cycles are possible.
|
---|