c++boost.gif (8819 bytes)HomeLibrariesPeopleFAQMore

7. Full Functoids

Table of Contents

7.1. Currying
7.2. Infix Syntax
7.3. Lambda Awareness
7.4. Smartness
7.5. Return-type deduction

In FC++, we use the term full functoid to describe functoids which are blessed with all of the special features of FC++. In this section we describe how to promote basic functoids (described at the end of Section 5) into full functoids, and we enumerate the added capabilities that full functoids have.

Full functoids are implemented using the fullN wrapper classes (as with the funN indirect functoid classes, N describes the arity of the function). Recall in Section 5 we defined mymap as a basic direct functoid like this:

   struct mymap_type {
      // a bunch of stuff
   } mymap;
Promoting mymap into a full functoid is straightforward:
   namespace impl {
      struct xmymap_type {
         // a bunch of stuff
      };
   }
   typedef full2<impl::xmymap_type> mymap_type;
   mymap_type mymap;
That is, rather than have mymap_type refer to the direct functoid struct we have defined, instead we make it a typedef for full2 instantiated with that type. (In FC++, we conventionally use a namespace called impl to store the actual definitions of basic direct functoids, and define the full functoids out in the main namespace. We also mangle the original name (prefixing an "x") in order to ensure that the basic functoid type will not be accidentally found instead of the full functoid type when various C++ name-lookup rules kick in.[3]) That's all there is to it.

Indirect functoids need no such promotion. Since the indirect functoid types (the funN classes) are defined by the library, they are already imbued with all of the full functoid features. We describe these features next.

(Now is a good time to mention that FC++ supports functoids of 0-3 parameters. The fullN, funN, and c_fun_type classes have only been defined for functions of up to three arguments.)

7.1. Currying

As described in Section 4, all full functoids exhibit built-in currying. For example, given a 3-argument full functoid "f", we can call it with any subset of its arguments, either by using underscores as placeholders, or by leaving off trailing arguments. Some examples:

   f(x,y,z)          // normal call
   f(x,_,z)          // yields a new unary functoid (expecting y)
   f(_,y,_)          // yields a new binary functoid (expecting x,z)
   f(x,y)            // yields a new unary functoid (expecting z)
   f(x)              // yields a new binary functoid (expecting y,z)
Additionally, all of the arguments can be curried, resulting in a "thunk" (a nullary functoid), by calling thunkN:
   thunk3(f,x,y,z)   // yields a new nullary functoid
Thunks will be described more in Section 8.

7.2. Infix Syntax

Binary and ternary full functoids can be called using a special infix syntax. This is syntactic sugar, as some functions "read" better using infix notation:

   plus(2,3)        // normal (prefix) function syntax
   2 ^plus^ 3       // FC++ infix syntax
For ternary functoids, note that infix syntax automatically invokes currying of the final argument:
   f(x,y,z)         // normal (prefix) function syntax
   f(x,y)           // as before: yields a new unary functoid (expecting z)
   x ^f^ y          // FC++ infix syntax (new unary functoid expecting z)
FC++ infix syntax was inspired by a similar construct in the Haskell programming language.

7.3. Lambda Awareness

Full functoids are lambda-aware. This means that they can be called with square brackets [] instead of round ones () to create lambda expressions. Lambda is discussed in Section 12.

7.4. Smartness

Full functoids exhibit another feature which we have chosen to label "smartness". Full functoids know how many arguments they can accept; a traits class provides access to this information. If F is a full functoid type, then these values are available at compile-time:

   functoid_traits<F>::template accepts<N>::args
   // A bool which says whether F can accept N arguments
   
   functoid_traits<F>::max_args
   // An int which says what the most arguments F can accept is
and also this operation is available
   functoid_traits<F>::template ensure_accepts<N>::args()
   // A no-op call that compiles only if F can accept N args
to ensure that compilation halts with an error if the specified function does not accept the expected number of arguments.

We use a traits class to provide literate error messages (along the lines of [McN&Sma00][Sie&Lum00]) so that some common abuses (like passing an F that is not a full functoid to functoid_traits) are caught within the library and a helpful identifier is injected into the compiler error message.

7.5. Return-type deduction

C++ will eventually have a typeof operator, but in the meantime, the standards committee has come up a stop-gap solution to return-type deduction [ReturnType]. The standard solution will be to use, e.g.,

   result_of<F(X,Y)>::type
to determine the result of an object of type F being called with arguments of type X and Y. The result_of template knows how to work on function pointers, monomorphic function objects, and polymorphic function objects.

FC++ basic functoids use a different convention for defining return-type deduction within the FC++ framework (described in Section 5). However, full functoids implement the standard convention (using result_of) as well. As a result, FC++ full functoids interoperate with other template libraries that require return-type-deduction facilities.



[3] This seems ugly, but Brian can't find a better way to effectively hide the names of basic functoids. He invites email advice/discussion on the topic.

Last revised: October 03, 2003 at 23:27:22 GMTCopyright © 2000-2003 Brian McNamara and Yannis Smaragdakis