Home | Libraries | People | FAQ | More |
In this section, we provide an overview of many of the major features of the FC++ library, through a number of short, illustrative examples.
FC++ functoids can be simultaneously higher order (able to take functoids as arguments and return them as results) and polymorphic[2] (template functions which work on a variety of data types). For example, consider the library function compose(), which takes two functoids and returns the composition:
// compose( f, g )(args) means f( g(args) )We could define a polymorphic functoid add_self(), which adds an argument to itself:
// add_self( x ) means x + xWe could then compose add_self with itself, and the result would still be a polymorphic functoid:
int x = 3; std::string s = "foo"; compose( add_self, add_self )( x ) // yields 12 compose( add_self, add_self )( s ) // yields "foofoofoofoo"Section 5 describes the infrastructure of these "direct functoids", which enables this feature to be implemented.
FC++ defines a lazy list data structure called list. FC++ lists are lazy in that they need not compute their elements until they are demanded. For example, the functoid enum_from() takes an integer and returns the infinite list of integers starting with that number:
list<int> li = enum_from( 1 ); // li is infinite list [1, 2, 3, ...]A number of functoids manipulate such lists; for instance map() applies a functoid to each element of a list:
li = map( add_self, li ); // li is now infinite list [2, 4, 6, ...]
The FC++ library defines a wealth of useful functoids and data types. There are named functoids for most C++ operators, like
plus(3,4) // 3+4 also minus, multiplies, etc.There are many functoids which work on lists, including map. Most of the list functions are identical those defined in Haskell. Additionally, a number of basic functions (like the identity function, id), combinators (like flip: flip(f)(x,y)==f(y,x)), and data types (like list and maybe; maybe will be discussed in Section 13) are designed to mimic exactly their Haskell counterparts. We also implement functoids for C++ constructs such as constructor calls and new calls:
construct3<T>()(x,y,z) // yields T(x,y,z) new2<T>()(x,y) // yields new T(x,y)and many more.
Functoids are curryable. That is, we can call a functoid with some subset of its arguments, returning a new functoid which expects the rest of the arguments. Currying of leading arguments can be done implicitly, as in
minus(3) // yields a new function "f(x)=3-x"Any argument can be curried explicitly using the placeholder variable _ (defined by FC++):
minus(3,_) // yields a new function "f(x)=3-x" minus(_,3) // yields a new function "f(x)=x-3"We can even curry all N of a function's arguments with a call to thunkN(), returning a thunk (a zero-argument functoid):
thunk2( minus, 3, 2 ) // yields a new thunk "f()=3-2"
FC++ functoids can be called using a special infix syntax (implemented by overloading operator^):
x ^f^ y // Same as f(x,y). Example: 3 ^plus^ 2This syntax was also inspired by Haskell; some function names (like plus) are more readable as infix than as prefix.
FC++ defines indirect functoids, which are function variables which can be bound to any function with the same (monomorphic) signature. Indirect functoids are implemented via the funN classes, which take N-1 template arguments describing the argument types, as well as a final template argument describing the result type. For example:
// Note: plus is polymorphic, the next line selects just "int" version fun2<int,int,int> f = plus; f(3,2); // yields 5 f = minus; f(3,2); // yields 1Indirect functoids are particularly useful in the implementation of callback libraries and some design patterns[Sma&McN02]. (Our indirect functoids are similar to boost::function objects; see Section 6.2 for a discussion of the differences.)
The FC++ library defines a few effect combinators. An effect combinator combines an effect (represented as a thunk) with another functoid. Here are some example effect combinators:
// before(thunk,f)(args) means { thunk(); return f(args); } // after(g,thunk)(args) means { R r = g(args); thunk(); return r; }An example: suppose you've defined a functoid write_log() which takes a string and writes it to a log file. Then
before( thunk1( write_log, "About to call foo()" ), foo )results in a new functoid with the same behavior as foo(), only it writes a message to the log file before calling foo().
FC++ interfaces with normal C++ code and the STL. The list class implements the iterator interface, so that lists can work with STL algorithms and other STL data structures can be converted into lists. The functoid ptr_to_fun() transforms normal C++ function pointers into functoids, and turns method pointers into functions which take a pointer to the receiver object as an extra first object. Here are some examples, which use currying to demonstrate that the result of ptr_to_fun is a functoid:
ptr_to_fun( &someFunc )(x)(y) // someFunc(x,y) ptr_to_fun( &Foo::meth )(aFooPtr)(x) // aFooPtr->meth(x)
FC++ also has a lambda sublanguage for defining anonymous functions on-the-fly, but we hold off describing this feature until Section 12.
Last revised: October 03, 2003 at 23:27:22 GMT | Copyright © 2000-2003 Brian McNamara and Yannis Smaragdakis |