FC++: Functional Tools for Object-Oriented Tasks
Total Page:16
File Type:pdf, Size:1020Kb
FC++: Functional Tools for Object-Oriented Tasks Yannis Smaragdakis and Brian McNamara College of Computing Georgia Institute of Technology Atlanta, GA 30332 {yannis,lorgon}@cc.gatech.edu http://www.cc.gatech.edu/~yannis/fc++ Abstract straightforwardly many common functional opera- tors (a large part of the Haskell Standard Prelude FC++ is a library for programming functionally in [19]). The library is currently quite rich in func- C++. Compared to other C++ functional program- tionality and has an efficient implementation. ming libraries, FC++ is distinguished by its power- ful type system which allows manipulating In a previous paper [16], we introduced FC++ and parametrically polymorphic functions (e.g., pass- its innovative type system, showed the components ing them as arguments to other functions and of the library, and demonstrated how its implemen- returning them as results). tation is more efficient than previous similar attempts. In this paper we show how to leverage In this paper, we show how FC++ can be used in the library to create simple, efficient, and safe common OO programming tasks. We demonstrate implementations of common OO design patterns FC++ implementations of several common design [8]. patterns (Adapter, Builder, Command, and more). Compared to conventional C++ implementations Certainly a lot has been written about language of these patterns, our implementations are either support for implementing design patterns (e.g., simpler (in that fewer classes/dependencies are [4][6]), functional techniques in OO programming, needed), more efficient, or more type-safe (thanks etc. Some of the approaches in the literature are to parametric polymorphism and type-inference). even very close in philosophy to our work. For instance: 1 Introduction • Alexandrescu [2] demonstrates how the meta- Functional and object-oriented programming are programming capabilities of the C++ language the most active fields of research in programming can be used to yield elegant pattern implemen- languages and methodologies. Several pieces of tations. work have attempted to connect the two paradigms. • Kühne’s dissertation proposes several patterns Among them are the Pizza language [18], extend- inspired by functional programming [14]. ing Java, as well as libraries for programming func- • Using functional techniques (higher-order tionally in C++ [11][12][15][21]. FC++ [16] is one functions) to implement the Observer and such library, distinguished from all others by its Command patterns is common—in fact, even powerful type system: FC++ allows the program- standard practice in Java and Smalltalk. mer to define and fully manipulate parametrically • The benefits of polymorphic and higher-order polymorphic functions. The conventional C++ way functions have often been discussed in the of representing polymorphic functions is via func- functional programming literature [22]. tion templates (e.g., see the C++ Standard Library Therefore, by necessity, part of our material is not [20]). Nevertheless, function templates suffer conceptually novel. In particular, Section 3 shows severe limitations—e.g., they cannot be passed as that FC++ offers a rich framework for OO tasks, parameters to other functions or returned as results. but similar results can be obtained with other lan- FC++ polymorphic functions overcome these limi- tations. This allows FC++ to re-implement 1 guages or libraries (although, among C++ and return them as results—even if they are poly- approaches, FC++ is arguably the most complete). morphic. There are two kinds of functoids: direct and indirect. Direct functoids are the usual repre- Section 4, however, shows new implementations of sentation for functions in FC++. Direct functoids common patterns by concentrating on some of the can be either monomorphic or polymorphic. Indi- more novel elements of FC++—mainly type infer- rect functoids, on the other hand, must always be ence and its ability to manipulate polymorphic monomorphic but can express first-class functions. functions. We see how parametric polymorphism That is, with indirect functoids, we can define vari- can find its way into some design patterns, how ables that range over all functions with the same FC++ can handle such tasks, and how using para- type signature. Thus, indirect functoids can be metric polymorphism results into more generic viewed as indirect function references, much like code. [Note that we do not discuss improvements C/C++ function pointers. In addition to direct and in design patterns’ implementations by the mere indirect functoids, FC++ provides a number of use- addition of parametric typing (e.g., C++ class tem- ful operations for creating functoids, composing plates) in a language. These are well understood them, specializing them, etc. We shall now discuss and are even discussed in [8], as implementation a few of the key components of FC++ in more suggestions.] detail. Although some C++ background is required for Indirect functoids are represented as the FunN fam- much of the paper, we believe that the principles ily of classes. FunNs specify function signatures via are interesting even to non-C++ programmers. In template parameters; N is the number of arguments. particular, the paper offers insights for language For example, Fun2<int,char,string> is the type designers and shows a platform where both sub- of a two-argument indirect functoid which takes an type polymorphism and parametric polymorphism int and a char and returns a string. Note that the with type inference are readily available as com- first N template arguments comprise the argument plementary tools for problem solving. types of the function, and the last template argu- ment is the result type. Thus, the simplest kind of 2 Background: Functional Programming indirect functoid is a Fun0<void>—a function that with FC++ takes no arguments and returns no result. (As we show in Section 3, this simplest functoid plays a We begin by introducing FC++. We divide this significant role in a number of patterns.) introduction into two parts. Section 2.1 gives an overview of the FC++ library, from the perspective A common way to create indirect functoids is with of a user of the library components. Section 2.2 ptr_to_fun. ptr_to_fun transforms a normal describes a few of the important implementation C++ function into a functoid. Here is a simple techniques of the FC++ library. These will be example, which also demonstrates how indirect employed in Section 4 and are necessary for the functoids can range over different functions: user who intends to write polymorphic functions using FC++. The ability to manipulate polymor- int i_times( int x, inty){return x*y;} phic functions is what sets FC++ apart from all int i_plus( int x, int y ) { return x+y;} ... other functional programming libraries in C++. For Fun2<int,int,int> f; a more complete introduction to FC++, the reader f = ptr_to_fun(&i_times); should refer to [16]. f(3,4); // returns 12 f = ptr_to_fun(&i_plus); 2.1 FC++ Basics f(3,4); // returns 7 In FC++, we express functions as instances of Note that f’s behavior depends on which functoid classes that follow certain conventions. We call it is bound to. This may seem reminiscent of OO such classes functoids. The key advantage to using dynamic dispatch (where a method call depends functoids (rather than C++ functions or function upon the dynamic type of the object that the templates) is that we can pass them as parameters receiver is bound to), and rightly so! There is just 2 such a virtual method call buried inside the imple- the int 3, and whose second field is the char ‘c’. mentation of all indirect functoids. Indeed, the C++ standard defines a template func- tion for the same purpose, which goes by the name Indirect functoids are more versatile than function std::make_pair. However, compared to mk_pair, pointers: they employ automatically currying, they std::make_pair suffers extreme limitations, by can be bound to new function objects that are cre- virtue of being defined as a template function. ated on-the-fly, and they exhibit a form of subtype Template functions cannot be passed as parame- polymorphism (see [16]). What follows will dem- ters, which means we cannot use the functional onstrate some of these features, which have impor- techniques mentioned above (i.e., currying and tant applications in Section 3 and Section 4. composition) on templates. Direct functoids avoid Currying is a functional technique that allows us to these limitations. For example, we can say curry2( mk_pair, 3 ) bind a subset of a function’s arguments to specific to return a new direct functoid which takes one values. For example, we can use curry to bind the argument of any type T, and returns a first argument of f to the value 1, creating a new std::pair<int,T> whose first field is 3. one-argument function: 2.2 Expressing Polymorphic FC++ Functoids Fun1<int,int> inc = curry2(f,1); inc(4); // returns 5 — i.e., i_plus(1,4) In the previous example, we demonstrated passing (The 2 in curry2 refers to the number of arguments a polymorphic functoid to a higher-order function that f expects.) In fact, FC++ also allows the curry- which returned a polymorphic result. How is this ing to be implicit—when a functoid is called with accomplished using C++? The trick in FC++ is to fewer actual arguments than it expects, it returns a use a struct with nested template members for both curried functoid. For instance, the previous exam- the actual function as well as an explicit represen- ple can be written more simply as: tation of the type signature of functoid, so that we can exploit the language’s type inference of func- Fun1<int,int> inc = f(1); tion arguments. Thus in FC++ we would define inc(4); // returns 5 — i.e., i_plus(1,4) mk_pair as: Although this simpler form is what a typical FC++ struct MkPair { user would write, we will avoid it in this paper, in template <class T, class U> order to emphasize that currying is done through std::pair<T,U> operator()( T x, U y ) { polymorphic functions (e.g., curry2) that manipu- return std::pair<T,U>(x,y); late other (possibly polymorphic) functions.