Introduction to Programming in ATS: | ||
---|---|---|
<<< Previous | Next >>> |
Code sharing is of paramount importance in programming language design. In a typed programming language, we often encounter a situation where the same functionality is needed for values of different types. For instance, we need a function to compute the length of a list while the elements in the list may be characters, integers, strings, etc. Evidently, we want to avoid implementing such a function for each element type as it would probably be the worst form of code duplication otherwise. We want to implement a single function that can be applied to any list to compute the length of the list. The length-computing function parameterizes over the element type of a list it is applied to, and it behaves uniformly regardless what the element type is. This is a form of code sharing that is given the name: parametric polymorphism, which should be distinguished from other forms of polymorphism such as inheritance polymorphism in object-oriented programming.
The code employed for illustration in this chapter plus some additional code for testing is available on-line.
A function template is a code template that implements a function. In the following code, two functions are defined to swap values:
typedef charint = (char, int) typedef intchar = (int, char) fun swap_char_int (xy: charint): intchar = (xy.1, xy.0) fun swap_int_char (xy: intchar): charint = (xy.1, xy.0) |
fun{a,b:t@ype} swap (xy: (a, b)): (b, a) = (xy.1, xy.0) fun swap_char_int (xy: charint): intchar = swap<char,int> (xy) fun swap_int_char (xy: intchar): charint = swap<int,char> (xy) |
A different style of implementation of swap is given as follows:
where the template parameters are given sequentially (instead of simultaneously). The following code shows how swap2 can be instantiated to for instances:fun swap_char_int (xy: charint): intchar = swap2<char><int> (xy) fun swap_int_char (xy: intchar): charint = swap2<int><char> (xy) |
As another example, a higher-order function template for composing (closure) functions is given as follows:
typedef cfun (t1:t@ype, t2:t@ype) = t1 -<cloref1> t2 fun{a,b,c:t@ype} compose (f: cfun (a, b), g: cfun (b, c)):<cloref1> cfun (a, c) = lam x => g(f(x)) // end of [compose] val plus1 = lam (x:int): int =<cloref1> x+1 val times2 = lam (x:int): int =<cloref1> x*2 val f_2x_1: cfun (int, int) = compose (times2, plus1) val f_2x_2: cfun (int, int) = compose (plus1, times2) |
In ATS, function templates are typechecked but not compiled. Only instances of a function template can be compiled. Suppose we have a function template foo taking one type parameter and two instances foo<T1> and foo<T2> are used in a program for some types T1 and T2. In general, one function in C is generated for each instance of foo when the program is compiled. However, if T1 and T2 have the same name, then the two instances share one function in C. In particular, if both T1 and T2 are boxed types, which are always given the same name, only one function in C is generated for them.
Please note that I may simply use the name function to refer to a function template from now on if no confusion is expected.
<<< Previous | Home | Next >>> |
Example: Evaluating Integer Expressions | Up | Polymorphic Functions |