-
Notifications
You must be signed in to change notification settings - Fork 25
/
chap4.tex
1970 lines (1670 loc) · 81.6 KB
/
chap4.tex
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\chapter{The Julia approach}
\section{Core calculus}
\label{sec:corecalc}
Julia is based on an untyped lambda calculus augmented with generic functions,
tagged data values, and mutable cells.
\vspace{-3ex}
\begin{singlespace}
\begin{align*}
e\ ::=\ &\ x & \textrm{(variable)} \\
&\ |\ 0\ |\ 1\ |\ \cdots & \textrm{(constant)} \\
&\ |\ x = e & \textrm{(assignment)} \\
&\ |\ e_1; e_2 & \textrm{(sequencing)} \\
&\ |\ e_1(e_2) & \textrm{(application)} \\
&\ |\ \texttt{if}\ e_1\ e_2\ e_3 & \textrm{(conditional)} \\
&\ |\ \texttt{new}(e_{tag}, e_1, e_2, \cdots) & \textrm{(data constructor)} \\
&\ |\ e_1.e_2 & \textrm{(projection)} \\
&\ |\ e_1.e_2 = e_3 & \textrm{(mutation)} \\
% &\ |\ (e) & \textrm{(grouping)} \\
&\ |\ \texttt{function}\ x_{name}\ e_{type}\ (x_1, x_2, \cdots)\ e_{body} & \textrm{(method definition)}
\end{align*}
\end{singlespace}
The \texttt{new} construct creates a value from a type tag and some other
values; it resembles the \texttt{Dynamic} constructor
in past work on dynamic typing \cite{Abadi:1991:DTS:103135.103138}.
The tag is determined by evaluating an expression.
%\footnote{Some agree that this qualifies as ``dynamic dependent typing''
% (personal communication with Jean Yang, 2014). Others contend that this
% terminology is not sensible.
%}
% TODO: say something about: it seems cumbersome to need to compute 2 parts
% for every value, but in practice it is easy to abstract away.
This means constructing types is part of programming, and types can
contain nearly arbitrary data.\footnote{We restrict this to values that can
be accurately compared by \texttt{===}, which will be introduced shortly.}
In Julia syntax, types are constructed using curly braces; this is shorthand
for an appropriate \texttt{new} expression
(tags are a specific subset of data values whose tag is the built-in value
\texttt{Tag}).
Although type expressions are quite often constants, one might also write
\texttt{MyType\{x+y\}}, to construct a \texttt{MyType} with
a parameter determined by computing \texttt{x+y}.
This provides a desirable trade-off for our intended applications:
\begin{itemize}
\item The compiler cannot always statically determine types.
\item Program behavior does not depend on the compiler's (in)ability to
determine types.
\item The compiler can do whatever it wants in an attempt to determine types.
\end{itemize}
The last point means that if the compiler is somehow able to statically
evaluate \texttt{x+y}, it is free to do so, and gain sharper type information.
This is a general property of dynamic type inference systems
\cite{kaplanullman,TICL,pticl,nimble,rubydust}.
Julia's types are designed to support this kind of inference, but they
are also used for code selection and specialization regardless of the
results of inference.
%This can be used to request specific kinds of objects from an API, or
%used as an implementation detail to tell the compiler which object
%properties to track.
In practice, \texttt{new} is always wrapped in a constructor function,
abstracting away the inconvenience of constructing both a type and a value.
In fact, \texttt{new} for user-defined data types is syntactically
available only within the code block that defines the type.
This provides some measure of data abstraction, since it is not possible
for user code to construct arbitrary instances.
Constants are pre-built tagged values.
Types are a superset of tags that includes values generated by the
special tags \texttt{Abstract}, \texttt{Union}, and \texttt{UnionAll},
plus the special values \texttt{Any} and \texttt{Bottom}:
\vspace{-3ex}
\begin{singlespace}
\begin{align*}
type\ ::=\ &\ \texttt{Bottom}\ |\ abstract\ |\ var \\
&\ |\ \texttt{Union}\ type\ type \\
&\ |\ \texttt{UnionAll}\ type\texttt{<:}var\texttt{<:}type\ type \\
&\ |\ \texttt{Tag}\ x_{name}\ abstract_{super}\ value* \\
abstract\ ::=\ &\ \texttt{Any}\ |\ \texttt{Abstract}\ x_{name}\ abstract_{super}\ value* \\
&\ |\ \texttt{Abstract}\ \texttt{Tuple}\ \texttt{Any}\ type*\ type\texttt{...}
\end{align*}
\end{singlespace}
\noindent
The last item is the special abstract variadic tuple type.
\subsubsection{Data model}
The language maps tags to data descriptions using built-in rules, and ensures that
the data part of a tagged value always conforms to the tag's description.
Mappings from tags to data descriptions are established by special type
declaration syntax.
Data descriptions have the following grammar:
\vspace{-3ex}
\begin{singlespace}
\begin{align*}
data\ ::=\ &\ bit^n\ |\ ref\ |\ data*
\end{align*}
\end{singlespace}
\noindent
where $bit^n$ represents a string of $n$ bits, and $ref$ represents a reference
to a tagged data value.
Data is automatically laid out according to the platform's application binary
interface (ABI).
Currently all compound types and heterogeneous tuples use the C struct ABI, and
homogeneous tuples use the array ABI.
Data may be declared mutable, in which case its representation is implicitly
wrapped in a mutable cell.
A built-in primitive equality operator \texttt{===} is provided, based on
$egal$ \cite{egal} (mutable objects are compared by address, and immutable objects
are compared by directly comparing both the tag and data parts bit-for-bit, and
recurring through references to other immutable objects).
\subsubsection{Functions and methods}
Functions are generally applied to more than one argument. In the application
syntax $e_1(e_2)$, $e_2$ is an implicitly constructed tuple of all arguments.
$e_1$ must evaluate to a generic function, and its most specific method
matching the tag of argument $e_2$ is called.
We use the keyword \texttt{function} for method definitions for the sake of
familiarity, though \texttt{method} is arguably more appropriate. Method
definitions subsume lambda expressions. Each method definition modifies a
generic function named by the argument $x_{name}$. The generic function to extend is
specified by its name rather than by the value of an expression in order to make it
easier to syntactically
restrict where functions can be extended. This, in turn, allows the language to
specify when new method definitions take effect, providing useful windows of
time within which methods do not change, allowing programs to be optimized more
effectively (and hopefully discouraging abusive and confusing run time
method replacements).
The signature, or specializer, of a method is obtained by evaluating $e_{type}$,
which must result in a type value as defined above. A method has $n$
formal argument names $x_i$. The specializer must be a subtype of the
variadic tuple type of length $n$. When a method is called, its formal argument
names are bound to corresponding elements of the argument tuple. If the
specializer is a variadic type, then the last argument name is bound to a
tuple of all trailing arguments.
% (TODO describe restrictions)
The equivalent of ordinary lambda expressions can be obtained by introducing
a unique local name and defining a single method on it.
Mutable lexical bindings are provided by the usual translation to operations
on mutable cells.
%\subsection{A note on static typing}
% isomorphism between our types T and propositions ``term will be of type T''
% we will elide the difference
% trivially undecidable due to the definition of new()
% it is quite likely a useful static version could be developed. but
% we do not do that here, since our goals are (1) to develop the system
% for specialization & selection, not checking, and (2) to emphasize
% that no amount of ``dynamism'' need be given up.
% static type systems begin with errors we want to exclude, then design
% restrictions to make that possible, then go on to show that, indeed,
% most useful programs can still be written.
% one reason we skip static checking is to reverse this process:
% first see what kinds of type behavior technical users want in their
% programs, then identify and quantify any regularities later.
\section{Type system}
\label{sec:typesystem}
Our goal is to design a type system for describing method applicability,
and (similarly) for describing classes of values for which to specialize code.
Set-theoretic types are a natural basis for such a system.
A set-theoretic type is a symbolic expression that denotes a set of values.
In our case, these correspond to the sets of values methods are intended to apply
to, or the sets of values supported by compiler-generated method specializations.
Since set theory is widely understood, the use of such types tends to be intuitive.
These types
are less coupled to the languages they are used with, since one may design
a value domain and set relations within it without yet considering how types
relate to program terms \cite{1029823, Castagna:2005:GIS:1069774.1069793}.
Since our goals only include
performance and expressiveness, we simply skip the later steps for now, and do
not consider in detail possible approaches to type checking.
A good slogan for this attitude might be ``check softly and carry a big
subtype relation.''
% TODO maybe mention that in some sense being dynamically typed makes the
% requirements on subtyping stricter, because we don't have the option
% of being conservative and giving more compiler errors than necessary.
% at every point, we have to pick some behavior and we can't silently
% do the wrong thing.
%To avoid the dual traps of ``excess power'' and divergence
The system we use
must have a decidable subtype relation, and must be closed under data flow operations
(meet, join, and widen).
It must also lend itself to a reasonable definition of
specificity, so that methods can be ordered automatically (a necessary property for
composability).
These requirements are fairly strict.
%, but still admit many possible designs.
%The one we present here is aimed at providing the minimum level of
%sophistication needed to yield a language that feels ``powerful'' to most modern
%programmers.
Beginning with the simplest possible system, we added features as
needed to satisfy the aforementioned closure properties.
% or to allow us to
%write method definitions that seemed particularly useful (as it turns out, these
%two considerations lead to essentially the same features).
%The presentation that
%follows will partially reproduce the order of this design process.
We will define our types by describing their denotations as sets.
We use the notation $\llbracket T \rrbracket$ for the set denotation of
type expression $T$.
Concrete language syntax and terminal symbols of the type expression grammar
are written in typewriter font, and metasymbols are written in mathematical italic.
First there is a universal type \texttt{Any}, an empty type \texttt{Bottom}, and
a partial order $\leq$:
\vspace{-3ex}
\begin{align*}
\llbracket \texttt{Any} \rrbracket &= \mathcal{D} \\
\llbracket \texttt{Bottom} \rrbracket &= \emptyset \\
T \leq S &\Leftrightarrow \llbracket T \rrbracket \subseteq \llbracket S \rrbracket
\end{align*}
\noindent
where $\mathcal{D}$ represents the domain of all values.
Also note that the all-important array type is written as \texttt{Array\{T,N\}} where
\texttt{T} is an element type and \texttt{N} is a number of dimensions.
Next we add data objects with structured tags.
The tag of a value is accessed with \texttt{typeof(x)}.
Each tag consists of a declared type name and some number of sub-expressions,
written as \texttt{Name\{}$E_1, \cdots, E_n$\texttt{\}}.
The center dots ($\cdots$) are meta-syntactic and represent a sequence of expressions.
Tag types may have declared supertypes (written as \texttt{super(T)}).
Any type used as a supertype must be declared as abstract, meaning it
cannot have direct instances.
\vspace{-3ex}
\begin{align*}
\llbracket \texttt{Name\{}\cdots\texttt{\}} \rrbracket &= \{ x\mid \texttt{typeof(}x\texttt{)} = \texttt{Name\{}\cdots\texttt{\}} \} \\
\llbracket \texttt{Abstract\{}\cdots\texttt{\}} \rrbracket &= \bigcup_{\texttt{super(}T\texttt{)} = \texttt{Abstract\{}\cdots\texttt{\}}} \llbracket T \rrbracket
\end{align*}
These types closely resemble the classes of an object-oriented language with
generic types, invariant type parameters, and no concrete inheritance.
We prefer parametric \emph{invariance} partly for reasons that have been addressed in the
literature \cite{Day:1995:SVC:217838.217852}.
Invariance preserves the property that the only subtypes of a concrete type are \texttt{Bottom}
and itself.
This is important given how we map types to data representations: an \texttt{Array\{Int\}}
cannot also be an \texttt{Array\{Any\}}, since those types imply different
representations (an \texttt{Array\{Any\}} is an array of pointers).
%If we tried to use covariance despite this, there would have to be some \emph{other}
%notion of which type a value \emph{really} had, which would be unsatisfyingly
%complex.
Tuples are a special case where covariance works, because each component type need
only refer to single value, so there is no need for concrete
tuple types with non-concrete parameters.
% TODO: important, but maybe conflates subtyping and inheritance too much
%Similarly, concrete inheritance conflicts somewhat with specialization.
%Code cannot be maximally specialized for a given type if instances of that type might
%have different representations.
Next we add conventional product (tuple) types, which are used to represent the
arguments to methods. These are almost identical to the nominal types defined above,
but are different in two ways: they are \emph{covariant} in their parameters, and permit
a special form ending in three dots (\texttt{...}) that denotes any number of trailing
elements:
\vspace{-3ex}
\begin{align*}
\llbracket \texttt{Tuple\{}P_1,\cdots,P_n\texttt{\}} \rrbracket &= \prod_{1\leq i \leq n} \llbracket P_i \rrbracket \\
\llbracket \texttt{Tuple\{}\cdots,P_n\texttt{...\}} \rrbracket, n\geq 1 &= \bigcup_{i\geq n-1} \llbracket \texttt{Tuple\{}\cdots,P_n^i\texttt{\}} \rrbracket
%\llbracket \texttt{Tuple\{}\cdots\texttt{\}} \rrbracket \cup \llbracket \texttt{Tuple\{}\cdots,P_n\texttt{\}} \rrbracket \cup \llbracket \texttt{Tuple\{}\cdots,P_n,P_n\texttt{...\}} \rrbracket \\
\end{align*}
\noindent
$P_n^i$ represents $i$ repetitions of the final element $P_n$ of the type expression.
Abstract tuple types ending in \texttt{...} correspond to variadic methods, which
provide convenient interfaces for tasks like concatenating any number of arrays.
Multiple dispatch has been formulated as dispatch on tuple types before \cite{Leavens1998}.
This formulation has the advantage that \emph{any} type that is a subtype of a
tuple type can be used to express the signature of a method.
It also makes the system simpler and more reflective, since subtype queries can be
used to ask questions about methods.
The types introduced so far would be sufficient for many programs, and are
roughly equal in power to several multiple dispatch systems that have been designed
before.
However, these types are not closed under data flow operations.
For example, when the two branches of a conditional expression yield different types,
a program analysis must compute the union of those types to derive the type of
the conditional.
The above types are not closed under set union.
We therefore add the following type connective:
\vspace{-3ex}
\[
\llbracket \texttt{Union\{}A,B\texttt{\}} \rrbracket = \llbracket A \rrbracket \cup \llbracket B \rrbracket \\
\]
As if by coincidence, \texttt{Union} types are also tremendously useful for expressing
method dispatch.
For example, if a certain method applies to all 32-bit integers regardless
of whether they are signed or unsigned, it can be specialized for \texttt{Union\{Int32,UInt32\}}.
\texttt{Union} types are easy to understand, but complicate the type system considerably.
To see this, notice that they provide an unlimited number of ways to rewrite any type.
For example a type \texttt{T} can always be rewritten as \texttt{Union\{T,Bottom\}}, or
\texttt{Union\{Bottom,Union\{T,Bottom\}\}}, etc.
Any code that processes types must ``understand'' these equivalences.
Covariant constructors (tuples in our case) also distribute over \texttt{Union} types,
providing even more ways to rewrite types:
\vspace{-3ex}
\[
\texttt{Tuple\{Union\{A,B\},C\}} = \texttt{Union\{Tuple\{A,C\},Tuple\{B,C\}\}}
\]
This is one of several reasons that union types are often considered undesirable.
When used with type inference, such types can grow without bound, possibly leading
to slow or even non-terminating compilation.
Their occurrence also typically corresponds to cases that would fail many static type
checkers.
Yet from the perspectives of both data flow analysis and method specialization, they
are perfectly natural and even essential
\cite{abstractinterp, Igarashi, Smith:2008:JTI:1449764.1449804}.
The next problem we need to solve arises from data flow analysis of
the \texttt{new} construct.
When a type constructor \texttt{C} is applied to a type
$S$ that is known only approximately at compile time, the type \texttt{C\{}$S$\texttt{\}}
does not correctly represent the result.
%if \texttt{C} is invariant.
The correct result would be the union of all types \texttt{C\{}$T$\texttt{\}}
where $T\leq S$.
There is again a corresponding need for such types in method dispatch.
Often one has, for example, a method that applies to arrays of any
kind of integer (\texttt{Array\{Int32\}}, \texttt{Array\{Int64\}}, etc.).
These cases can be expressed using a \texttt{UnionAll} connective, which denotes
an iterated union of a type expression for all values of a parameter within
specified bounds:
\vspace{-3ex}
\[
\llbracket \texttt{UnionAll }L\texttt{<:T<:}U\ A \rrbracket = \bigcup_{L \leq T \leq U} \llbracket A[T/\texttt{T}] \rrbracket
\]
\noindent
where $A[T/\texttt{T}]$ means $T$ is substituted for \texttt{T} in expression $A$.
% TODO: The inclusion of lower bounds might make subtyping undecidable?
% Note that giving up lower bounds might permit intersections or arrows,
% but we prefer lower bounds.
This is similar to an existential type \cite{boundedquant};
for each concrete subtype of it there exists a corresponding $T$.
Anecdotally, programmers often find existential types confusing.
We prefer the union interpretation because we are describing sets of values;
the notion of ``there exists'' can be semantically misleading since it sounds like
only a single $T$ value might be involved.
However we will still use $\exists$ notation as a shorthand.
%Conjecture: these types are intuitive to dispatch on because they correspond
%to program behavior in the same way that data flow analysis approximates program
%behavior.
% $T=S \longleftrightarrow (T\leq S) \wedge (S\leq T)$.
\subsubsection{Examples}
\texttt{UnionAll} types are quite expressive. In combination with nominal
types they can describe groups of containers such as
\texttt{UnionAll T<:Number Array\{Array\{T\}\}} (all arrays of arrays of
some kind of number) or
\texttt{Array\{UnionAll T<:Number Array\{T\}\}} (an array of arrays of
potentially different types of number).
In combination with tuple types, \texttt{UnionAll} types provide powerful
method dispatch specifications. For example
\texttt{UnionAll T Tuple\{Array\{T\},Int,T\}} matches three arguments:
an array, an integer, and a value that is an instance of the array's
element type. This is a natural signature for a method that assigns a
value to a given index within an array.
\subsubsection{Type system variants}
Our design criteria do not identify a unique type system; some
variants are possible.
The following features would probably be fairly straightforward to add:
\vspace{-3ex}
\begin{singlespace}
\begin{itemize}
\item Structurally subtyped records
\item $\mu$-recursive types (regular trees)
\item General regular types (allowing \texttt{...} in any position)
\end{itemize}
\end{singlespace}
\noindent
The following features would be difficult to add, or possibly break decidability
of subtyping:
\vspace{-3ex}
\begin{singlespace}
\begin{itemize}
\item Arrow types
\item Negations
\item Intersections, multiple inheritance
\item Universal quantifiers
\item Contravariance
%\item arbitrary predicates, theory of natural numbers, etc.
\end{itemize}
\end{singlespace}
\subsection{Type constructors}
It is important for any proposed high-level technical computing language to be
simple and approachable, since otherwise the value over established
powerful-but-complex languages like C++ is less clear.
In particular, type parameters raise usability concerns.
Needing to write parameters along with every type is verbose, and requires users
to know more about the type system and to know more details of particular
types (how many parameters they have and what each one means).
Furthermore, in many contexts type parameters are not directly relevant.
For example, a large amount of code operates on \texttt{Array}s of any
element type, and in these cases it should be possible to ignore type parameters.
Consider \texttt{Array\{T\}}, the type of arrays with element type \texttt{T}.
In most languages with parametric types, the identifier \texttt{Array} would
refer to a type constructor, i.e.\ a type of a different \emph{kind} than
ordinary types like \texttt{Int} or \texttt{Array\{Int\}}.
Instead, we find it intuitive and appealing for \texttt{Array} to refer to
any kind of array, so that a declaration such as \texttt{x::Array} simply
asserts \texttt{x} to be some kind of array.
In other words,
\vspace{-3ex}
\[
\texttt{Array} = \texttt{UnionAll T Array$^\prime$\{T\}}
\]
\noindent
where \texttt{Array$^\prime$} refers to a hidden, internal type constructor.
The \texttt{\{ \}} syntax can then be used to instantiate a \texttt{UnionAll}
type at a particular parameter value.
\subsection{Subtyping}
\label{sec:subtyping}
\begin{figure}[!t]
\begin{center}
\def\arraystretch{2}
\begin{tabular}{|c|}\hline
\begin{tabular}{ccc}
\AxiomC{$_A^B X^L, \Gamma\ \vdash\ T \leq S$}
\UnaryInfC{$\Gamma\ \vdash\ \exists$ $_A^B X\ T\ \leq\ S$}
\DisplayProof
\hspace{3ex}
&
\AxiomC{$_A^BX^R, \Gamma\ \vdash\ T \leq S$}
\UnaryInfC{$\Gamma\ \vdash\ T\ \leq\ \exists$ $_A^B X\ S$}
\DisplayProof
\hspace{3ex}
&
\AxiomC{}
\UnaryInfC{$\Gamma\ \vdash\ X \leq X$}
\DisplayProof
\end{tabular}
\\[8pt]
\begin{tabular}{cc}
\AxiomC{$^BX^L,{} _AY^L,\Gamma\ \vdash\ B \leq Y\ \vee\ X \leq A$}
\UnaryInfC{$^BX^L,{} _AY^L,\Gamma\ \vdash\ X \leq Y$}
\DisplayProof
\hspace{4ex}
&
\AxiomC{$_A^BX^R,{} Y^R,\Gamma\ \vdash\ B \leq A$}
\UnaryInfC{$_A^BX^R,{} Y^R,\Gamma\ \vdash\ X \leq Y$}
\DisplayProof
\\[8pt]
\AxiomC{$_A^BX^R,\Gamma\ \vdash\ T \leq B$}
\UnaryInfC{$_{A \cup T}^{\ \ \ B}X^R,\Gamma\ \vdash\ T \leq X$}
\DisplayProof
\hspace{4ex}
&
\AxiomC{$_A^BX^R,\Gamma\ \vdash\ A \leq T$}
\UnaryInfC{$_A^TX^R,\Gamma\ \vdash\ X \leq T$}
\DisplayProof
\\[8pt]
\AxiomC{$_AX^L,\Gamma\ \vdash\ T \leq A$}
\UnaryInfC{$_AX^L,\Gamma\ \vdash\ T \leq X$}
\DisplayProof
\hspace{3ex}
&
\AxiomC{$^BX^L,\Gamma\ \vdash\ B \leq T$}
\UnaryInfC{$^BX^L,\Gamma\ \vdash\ X \leq T$}
\DisplayProof
\\[8pt]
\end{tabular}
\\
\hline
\end{tabular}
\end{center}
\caption[Subtyping algorithm]{
\small{
Subtyping algorithm for \texttt{UnionAll} ($\exists$) and variables.
$X$ and $Y$ are variables, $A$, $B$, $T$, and $S$ are types or variables.
$_A^BX$ means $X$ has lower bound $A$ and upper bound $B$.
$^R$ and $^L$ track whether a variable originated on the right or on the left of
$\leq$.
Rules are applied top to bottom.
Summary of the sequent notation used:
$\Gamma\vdash x$ means that $x$ is true in environment $\Gamma$.
Given the premises above a horizontal line, we can derive the conclusion
below it.
}
}
\label{subtvars}
\end{figure}
Computing the subtype relation is the key algorithm in our system.
It is used in the following cases:
\begin{itemize}
\item Determining whether a tuple of arguments matches a method signature.
\item Comparing method signatures for specificity and equality.
\item Source-level type assertions.
\item Checking whether an assigned value matches the declared type of a
location.
\item Checking for convergence during type inference.
\end{itemize}
Deciding subtyping for base types is straightforward: \texttt{Bottom} is
a subtype of everything, everything is a subtype of \texttt{Any}, and
tuple types are compared component-wise.
The invariant parameters of tag types are compared in both directions: to check
$\texttt{A\{B\}}\leq \texttt{A\{C\}}$, check $\texttt{B}\leq\texttt{C}$ and
then $\texttt{C}\leq\texttt{B}$.
In fact, the algorithm depends on these checks being done in this order, as we
will see in a moment.
Checking union types is a bit harder. When a union $A\cup B$ occurs in the
algorithm, we need to non-deterministically replace it with either $A$ or
$B$. The rule is that for all such choices on the left of $\leq$, there
must exist a set of choices on the right such that the rest of the
algorithm accepts. This can be implemented by keeping a stack of
decision points, and looping over all possibilities with an outer
for-all loop and an inner there-exists loop. We speak of ``decision points''
instead of individual unions, since in a type like \texttt{Tuple\{Union\{A,B\}...\}}
a single union might be compared many times.
The algorithm for \texttt{UnionAll} and variables is shown in figure~\ref{subtvars}.
The first row says to handle a \texttt{UnionAll} by extending the environment
with its variable, marked according to which side of $\leq$ it came from,
and then recurring into the body.
In analogy to union types, we need to check that for all variable values on
the left, there exists a value on the right such that the relation holds.
The for-all side is relatively easy to implement, since we can just use
a variable's bounds as proxies for its value (this is shown in
the last row of the figure).
We implement the there-exists side by narrowing a variable's bounds
(raising the lower bound and lowering the upper bound, in figure row 3).
The relation holds
as long as the bounds remain consistent (i.e.\ lower bound $\leq$
upper bound).
The algorithm assumes that all input types are well-formed,
which includes variable lower bounds always being less than or equal to
upper bounds.
The rules in the second row appear asymmetric.
This is a result of exploiting the lack of contravariant constructors.
No contravariance means that every time a
right-side variable appears on the \emph{left} side of a comparison,
it must be because it occurs in invariant position, and the steps outlined
in the first paragraph of this section have ``flipped'' the order
(comparing both $\texttt{B}\leq\texttt{C}$ and $\texttt{C}\leq\texttt{B}$).
This explains the odd rule for comparing two right-side variables:
this case can only occur with differently nested \texttt{UnionAll}s and
invariant constructors, in which case the relation holds only if
all involved bounds are equal.
By symmetry, one would expect the rule in row 3, column 2 to
update $X$'s upper bound to $B\cap T$. But because of invariance,
$T\leq B$ has already been checked by rule in row 3, column 1.
Therefore $B\cap T = T$. This is the reason the ``forward''
direction of the comparison needs to be checked first: otherwise,
we would have updated $B$ to equal $T$ already and the $T\leq B$
comparison would become vacuous. Alternatively, we could actually
compute $B\cap T$. However there is reason to suspect that
trouble lies that way. We would need to either add intersection types
to the system, or compute a meet without them. Either way, the
algorithm would become much more complex and, judging by past
results, likely undecidable.
% worth asking why we go through such contortions, only to end up
% setting X's bounds both to T exactly. the reason is that this
% handles covariant and invariant position together, and also
% automatically handles ``degrees of freedom'' mismatches like
% Pair{T,T} < Pair{T,S}, which by the way doesn't hold if the
% variables have equal upper and lower bounds.
%TODO: termination and correctness.
\subsubsection{Complexity}
These subtyping rules are likely $\Pi_2^{\textrm{P}}$-hard.
Checking a subtype relation with unions requires checking that for all choices
on the left, there exists a choice on the right that makes the relation hold.
This matches the quantifier structure of 2-TQBF problems of the form
$\forall x_i \exists y_i F$ where $F$ is a boolean formula.
If the formula is written in conjunctive normal form, it corresponds to subtype
checking between two tuple types, where the relation must hold for each pair of
corresponding types.
Now use a type \texttt{N\{}$x$\texttt{\}} to represent $\neg x$.
The clause $(x_i \vee y_i)$ can be translated to
$x_i\ \texttt{<:\ Union\{N\{}y_i\texttt{\}, True\}}$ (where the $x_i$ and
$y_i$ are type variables bound by \texttt{UnionAll} on the left and right,
respectively).
We have not worked out the details, but this sketch is reasonably
convincing.
$\Pi_2^{\textrm{P}}$ is only the most obvious reduction to try; it is possible our
system equals PSPACE or even greater, as has often been the case for subtyping
systems like ours.
\subsubsection{Implementation}
Appendix~\ref{appendix:subtyping} gives a Julia implementation of this
algorithm.
This also serves as a good example of Julia type and method definition
syntax.
Translating the code into other languages should be straightforward.
\subsubsection{Example deductions}
We will briefly demonstrate the power of this algorithm through examples of
type relationships it can determine.
In these examples, note that $<$ means less than but not equal.
\texttt{Pair} is assumed to be a tag type with two parameters.
\noindent
The algorithm can determine that a type matches constraints specified by another:
\vspace{-5ex}
\[
\texttt{Tuple}\{\texttt{Array}\{\texttt{Integer},1\}, \texttt{Int}\}\ <\
(\exists\ T<:\texttt{Integer}\ \exists\ S<:T\ \texttt{Tuple}\{\texttt{Array}\{T,1\},S\})
\]
\vspace{-1ex}
\noindent
It is not fooled by redundant type variables:
\vspace{-2ex}
\[
\texttt{Array}\{\texttt{Int},1\}\ =\
\texttt{Array}\{(\exists\ T<:\texttt{Int}\ T), 1\} \]
\noindent
Variables can have non-trivial bounds that refer to other variables:
\vspace{-3ex}
\begin{singlespace}
\begin{align*}
&\texttt{Pair}\{\texttt{Float32},\texttt{Array}\{\texttt{Float32},1\}\}\ <\ \\
&\hspace{4ex}\exists\ T<:\texttt{Real}\ \exists\ S<:\texttt{AbstractArray}\{T,1\}\ \texttt{Pair}\{T,S\}
\end{align*}
\end{singlespace}
% @test !issub(Ty((Float32,Array{Float64,1})),
% @UnionAll T<:Ty(Real) @UnionAll S<:inst(AbstractArrayT,T,1) tupletype(T,S))
\noindent
In general, if a variable appears multiple times then its enclosing type is
more constrained than a type where variables appear only once:
\vspace{-3ex}
\[
\exists\ T\ \texttt{Pair}\{T,T\}\ <\ \exists\ T\ \exists\ S\ \texttt{Pair}\{T,S\}
\]
\noindent
However with sufficiently tight bounds that relationship no longer holds
(here $x$ is any type):
\vspace{-3ex}
\[
\exists\ x<:T<:x\ \exists\ x<:S<:x\ \texttt{Pair}\{T,S\}\ <\ \exists\ T\ \texttt{Pair}\{T,T\}
\]
% @test issub_strict((@UnionAll T @UnionAll S<:T inst(PairT,T,S)),
% (@UnionAll T @UnionAll S inst(PairT,T,S)))
\noindent
Variadic tuples of unions are particularly expressive (``every argument is
either this or that''):
\vspace{-3ex}
\begin{singlespace}
\begin{align*}
&\texttt{Tuple\{Vector\{Int\},Vector\{Vector\{Int\}\},Vector\{Int\},Vector\{Int\}\}}\ <\ \\
&\hspace{4ex}\exists\ S<:\texttt{Real}\ \texttt{Tuple\{Union\{Vector\{}S\}\texttt{,Vector\{Vector\{}S\texttt{\}\}\}...\}}
\end{align*}
\end{singlespace}
\noindent
And the algorithm understands that tuples distribute over unions:
\vspace{-3ex}
\[
\texttt{Tuple\{Union\{A,B\},C\}}\ =\ \texttt{Union\{Tuple\{A,C\},Tuple\{B,C\}\}}
\]
\subsubsection{Pseudo-contravariance}
Bounded existential types are normally contravariant in their lower bounds.
However, the set denotations of our types give us
$(\exists\ T>:X\ T) = \texttt{Any}$.
By interposing an invariant constructor, we obtain a kind of contravariance:
\[
(\exists\ T>:X\ \texttt{Ref}\{T\}) \leq (\exists\ T>:Y\ \texttt{Ref}\{T\}) \implies Y\leq X
\]
\noindent
Types like this may be useful for describing nominal arrow types
(see section~\ref{sec:nominalarrows}).
This ability to swap types between sides of $\leq$ is one of the keys to
the undecidability of subtyping with bounded quantification~\cite{Pierce1994131}.
Nevertheless we conjecture that our system is decidable, since we treat
left- and right-side variables asymmetrically.
The bounds of left-side variables do not change, which might be sufficient to
prevent the ``re-bounding'' process in system $F_{<:}$ from continuing.
%We conjecture that the presence of the invariant constructor blocks
%undecidability in our case.
\subsubsection{Related work}
Our algorithm is similar to some that have been investigated for Java
generics (\cite{wehr2008subtyping, Cameron:2009:SWE:1557898.1557902,
Tate:2011:TWJ:1993316.1993570}).
In that context, the primary difficulty is undecidability due to
circular constraints such as \texttt{class Infinite<P extends Infinite<?>>}
and circularities in the inheritance hierarchy such as
\texttt{class C implements List<List<?\ super C>>} (examples from
\cite{Tate:2011:TWJ:1993316.1993570}).
We have not found a need for such complex declarations.
Julia disallows mentioning a type in the constraints of its own parameters.
When declaring type $T$ a subtype of $S$, only the direct parameters of
$T$ or $S$ can appear in the subtype declaration.
We believe this leads to decidable subtyping.
Dynamic typing provides a useful loophole: it is never truly
necessary to declare constraints on a type's parameters, since
the compiler does not need to be able to prove that those
parameters satisfy any constraints when instances of the type are
used.
In theory, an analogous problem for Julia would be spurious method
ambiguities due to an inability to declare sufficiently strong
constraints.
More work is needed to survey a large set of Julia libraries to see
if this might be a problem in practice.
Past work on subtyping with regular
types~\cite{hosoya2000regular, xtatic} is also related, particularly
to our treatment of union types and variadic tuples.
\section{Dispatch system}
% TODO \cite{Dubois:1995:EP:199448.199473}
Julia's dispatch system strongly resembles the multimethod systems
found in some object-oriented languages
\cite{closspec,closoverview,dylanlang,cecil,cecilspec,chambers2006diesel}.
However we prefer the term type-based dispatch, since our system
actually works by dispatching a \emph{single tuple type} of arguments.
The difference is subtle and in many cases not noticeable, but has
important conceptual implications.
It means that methods are not necessarily restricted to specifying
a type for each argument ``slot''.
For example a method signature could be
\texttt{Union}\{\texttt{Tuple}\{\texttt{Any},\texttt{Int}\}, \texttt{Tuple}\{\texttt{Int},\texttt{Any}\}\},
which matches calls where either, but not necessarily both, of two
arguments is an \texttt{Int}.
\footnote{Our implementation of Julia does not yet have syntax for such methods.}
\subsection{Type and method caches}
The majority of types that occur in practice are \emph{simple}.
A simple type is a tag, tuple, or abstract type, all of whose parameters
are simple types or non-type values.
For example \texttt{Pair\{Int,Float64\}} is a simple type.
Structural equality of simple types is equivalent to type equality,
so simple types are easy to compare, hash, and sort.
Another important set of types is the \emph{concrete} types, which
are the direct types of values.
Concrete types are hash-consed in order to assign a unique integer
identifier to each.
These integer identifiers are used to look up methods efficiently
in a hash table.
Some types are concrete but not simple, for example
\texttt{Array\{Union\{Int,String\},1\}}.
The hash-consing process uses linear search for these types.
Fortunately, such types tend to make up less than 10\% of the total
type population.
On cache misses, method tables also use linear search.
%this can work especially well for tuple types.
%by writing (1, 2.0) you immediately obtain an efficient ``struct'' type.
%cases like protocol compilers require generating code in advance.
%this generates it on the fly, but can pick up and reuse any cached
%code that might happen to exist for a particular tuple type.
\subsection{Specificity}
Sorting methods by specificity is a crucial feature of a generic
function system.
It is not obvious how to order types by specificity, and our
rules for it have evolved with experience.
The core of the algorithm is straightforward:
\begin{itemize}
\item If $A$ is a strict subtype of $B$, then it is more specific than $B$.
\item If $B\leq A$ then $A$ is not more specific than $B$.
\item If some element of tuple type $A$ is more specific than its corresponding
element in tuple type $B$, and no element of $B$ is more specific than its
corresponding element in $A$, then $A$ is more specific.
\end{itemize}
%tuples: if an elt of a is more specific than its corresponding elt in b,
%and no elt of b is more specific than its corresponding elt in a.
This is essentially the same specificity rule used by Dylan~\cite{dylanlang}
for argument lists.
Julia generalizes this to tuple types; it is applied recursively to tuple
types wherever they occur.
We then need several more rules to cover other features of the type system:
\begin{itemize}
\item A variadic type is less specific than an otherwise equal non-variadic type.
\item Union type $A$ is more specific than $B$ if some element of $A$ is
more specific than $B$, and $B$ is not more specific than any
element of $A$.
\item Non-union type $A$ is more specific than union type $B$ if it is more specific
than some element of $B$.
\item A tag type is more specific than a tag type strictly above it in the
hierarchy, regardless of parameters.
%(this embeds a moderate amount of ``class based'' dispatch, compatible
%with programmer intuition)
\item A variable is more specific than type $T$ if its upper bound is more specific
than $T$.
\item Variable $A$ is more specific than variable $B$ if $A$'s upper bound is
more specific than $B$'s, and $A$'s lower bound is not more specific than $B$'s.
\end{itemize}
So far, specificity is clearly a less formal notion than subtyping.
Specificity and method selection implicitly add a set difference operator to
the type system.
When a method is selected, all more specific definitions have not been, and
therefore the more specific signatures have been subtracted.
For example, consider the following definitions of a string concatenation
function:
\begin{singlespace}
\begin{lstlisting}[language=julia]
strcat(strs::ASCIIString...) = # an ASCIIString
strcat(strs::Union{ASCIIString,UTF8String}...) = # a UTF8String
\end{lstlisting}
\end{singlespace}
Inside the second definition, we know that at least one argument must be a
\texttt{UTF8String}, since otherwise the first definition would have
been selected.
This encodes the type behavior that ASCII strings are closed under
concatenation, while introducing a single UTF-8 string requires the result
to be UTF-8 (since UTF-8 can represent strictly more characters than ASCII).
The effect is that we obtain some of the power of set intersection and
negation without requiring subtyping to reason explicitly about such types.
%The tradeoff is a slight loss of type precision when analyzing such
%definitions.
\subsection{Parametric dispatch}
We use the following syntax to express methods whose signatures
are \texttt{UnionAll} types:
\begin{singlespace}
\begin{lstlisting}[language=julia]
func{T<:Integer}(x::Array{T}) = ...
\end{lstlisting}
\end{singlespace}
\noindent
This method matches any \texttt{Array} whose element type is some subtype
of \texttt{Integer}.
The value of \texttt{T} is available inside the function.
This feature largely overcomes the restrictiveness of parametric invariance.
Methods like these are similar to methods with ``implicit type parameters''
in the Cecil language~\cite{cecilspec}.
However, Cecil (along with its successor, Diesel~\cite{chambers2006diesel})
does not allow methods to differ only in type parameters.
In the semantic subtyping framework we use, such definitions have a
natural interpretation in terms of sets, and so are allowed.
In fact, they are used extensively in Julia (see section~\ref{sec:callingblas}
for an example).
The system $\textrm{ML}_{\leq}$~\cite{Bourdoncle:1997:TCH:263699.263743} combined
multiple dispatch with parametric polymorphism, however the parametricity
appeared in types assigned to entire generic functions.
This has benefits for program structure and type safety, but is a bit
restrictive.
In fact Julia does not directly use the concept of parametricity.
Like Fortress~\cite{fortressmodular}, we allow more flexibility in method
definitions.
Our formulation of method signatures as existential types for purposes of
applicability and specificity is similar to that used in Fortress.
However unlike Fortress all of our types are first-class: all types
(including union types) can be dispatched on and used in declarations.
\subsection{Diagonal dispatch}
\label{sec:diagonal}
\texttt{UnionAll} types can also express constraints between arguments.
The following definition matches two arguments of the same concrete
type:
\begin{singlespace}
\begin{lstlisting}[language=julia]
func{T}(x::T, y::T) = ...
\end{lstlisting}
\end{singlespace}
\noindent
Section~\ref{sec:conversion} discusses an application.
This feature is currently implemented only in the dispatch system;
we have not yet formalized it as part of subtyping.
For example, the subtyping algorithm presented here concludes that
\texttt{Tuple\{Int,String\}} is a subtype of
\texttt{UnionAll T Tuple\{T,T\}}, since \texttt{T} might equal
\texttt{Any} or \texttt{Union\{Int,String\}}.
We believe this hurts the accuracy of static type deductions, but
not their soundness, as long as we are careful in the compiler to
treat all types as over-estimates of run time types.
In any case, we plan to fix this by allowing some type variables
to be constrained to concrete types.
This only affects type variables that occur only in covariant
position; types such as \texttt{UnionAll T Tuple\{Array\{T\},T\}}
are handled correctly.
\subsection{Constructors}
\label{sec:constructors}
Generic function systems (such as Dylan's) often implement value
constructors by allowing methods to be specialized for classes
themselves, instead of just instances of classes.
We extend this concept to our type system using a construct similar