FC++ -- reminder formal review ends today at midnight PST

Please remember that the formal review for FC++ ends today at midnight PST. Please post your reviews, comments or questions today. Thanks, Mat ### once again the original notice reproduced below for your convenience The formal review of fcpp by Brian McNamara and Yannis Smaragdakis is now in progress, running from Friday February 13th at 12 noon, PST and and run to Monday, February 23rd, 12 noon PST. The library is currently available at <http://www.cc.gatech.edu/~yannis/fc++/boostpaper/> Here is a brief introduction to the library: FC++ is a library for doing functional programming in C++. The library provides a general framework to support various functional programming aspects, such as higher-order polymorphic functions, currying, lazy evaluation, and lambda. In addition to the framework, FC++ also provides a large library of useful functions and data types. The main utility of the library comes from its ability to implement functional programming designs, especially those which use higher-order polymorphic functions or lazy evaluation in significant ways. The library also contains a number of ancillary components that implement features like currying and lambda for function objects; these features are also useful in their own right. For information about submitting a Formal Review, see <http://www.boost.org/more/formal_review_process.htm> Please try out the library and read the documentation and say whether or not you think the library should be accepted by Boost and why. Compiler notes: This library has been tested with icc7 and with gcc-3.1.1 (and various other gcc-3.x.y versions) on Solaris and Linux. The library is also likely to compile on VC++7.1 and Comeau (prior versions of the library succeeded with these compilers, but this particular snapshot has not been tested with them). The library covers a great deal of the C++ language and thus requires a high degree of standard conformance; compilers like gcc-2.95.x and VC++6 will not be able to compile the library. Getting started: The web page http://www.cc.gatech.edu/~yannis/fc++/boostpaper/ mentions all the basics: The zip file will expand into these subdirectories: fcpp/fcpp/ # the library (.hpp) files fcpp/fcpp_clients/ # the example (.cpp) files Running the examples just involves invoking the complier with the right #include paths; for example, # in the fcpp_clients directory g++ -I/path/to/boost -I../fcpp some_example.cpp There is also a gnu-style Makefile included with the clients (examples), which will compile all of the examples in batch (you will have to edit a tiny bit at the top of the Makefile). The examples in the clients directory are a mix of both (regression-type) tests cases which cover the features of the library and example applications which demonstrate some of the library's utility. The README file in the client directory gives a short explanation of some of the more interesting clients. The documentation at http://www.cc.gatech.edu/~yannis/fc++/boostpaper/ provides a walkthrough and reference for most of the main features of the library. There are also some pointers out to other papers and documentation on the main FC++ web site: http://www.cc.gatech.edu/~yannis/fc++/ Conformance to Boost guidelines: The library meets most of the requirements described at http://www.boost.org/more/lib_guide.htm Here are a few notable exceptions: - the current library headers are too "monolithic"; you #include "prelude.hpp" and suck in all of the library - some of the lambda internals should be rewritten to use MPL rather than hand-rolled metaprogramming code - only a few of the example programs utilize the Boost testing framework (to automate regression testing) Should the library be accepted into Boost, Brian will remedy these deficiencies. (If you notice other major problems along these lines, please bring them to Brian's attention during the review.) The Formal Review manager is Mat Marcus.

I don't know what the boost rules are as far as random strangers giving drive-by reviews (do you have to be a member or anything-- feel free to ignore me if I'm out of place), but I thought I'd chime in in case I accidently say something useful. This probably isn't in-depth enough to be a real review, but from what I've seen of FC++, if I had a vote, I would vote to accept FC++ into boost, conditional on the author fixing some things up (non-const refs, dropping the N from fullN, and many of the suggestions by Jonathan Turkanis). * What is your evaluation of the design? To help me evaluate the design, I wrote a few quick client programs to try and get a feel for the 'language'. The most instructive of these, was a quicksort function, modelled after the example given on the haskell home page ( http://www.haskell.org/aboutHaskell.html ): template<typename ordT> struct quicksort : public c_fun_type<list<ordT>, list<ordT> > { list<ordT> operator()(list<ordT> in) const { if(null(in)) return NIL; lambda_var<1> X; lambda_var<2> pivot; lambda_var<3> tl; lambda_var<4> lefts; lambda_var<5> rights; lambda_var<6> qs_rec; return lambda()[ let [ pivot == head[in], tl == tail[in], lefts == comp_m<list_m>()[X | X <= tl, guard[X %less% pivot]], rights == comp_m<list_m>()[X | X <= tl, guard[X %greater_equal% pivot]], /* (A) */ qs_rec == full1<quicksort<ordT> >() ].in[ /* (B) */ qs_rec[lefts] %cat% (pivot %cons% qs_rec[rights]) ] ](); } }; I've been generally impressed with the design. There are a few things that bother me. I dislike having to manually promote direct functoids to full functoids. This is particularly sticky if the functoid is recursive, as above. I worked around this at (A) by using a lambda variable to hold the full functoid, but I would have thought there was a better way. If there is, I didn't see it in the documentation. I also could have had a member typedef quicksort::full or something, but I find that even worse. On a positive note, I really like the explicit lambda syntax. Most of what I've seen from others has stated that it's not as easy to use as boost::lambda, or that it's too verbose. I disagree-- I found it very frustrating to read the boost::lambda documentation and try and remember all these little rules about when to use protect() or unlambda() or constant() or make_const() or whatever. Further, the explicit lambda syntax is much more convienant when constructing nested lambdas, since you can still access variables in the outer scope. It's unfortunate that the variables require a unique integer template parameter, but I don't think this really limits their usefulness. One thing in particular that seems to have been overlooked by some others about the lambda variables is that they allow you to do a fair amount of type inference! Looking again at the quicksort example, rather than worry about types, I just declared everything lambda and the compiler was able to figure everything out. This is true even for qs_rec, which is bound to the full-functoid version of quicksort to be used in the recursive step at (B). FC++ is able to figure all this out, even though qs_rec isn't bound when declared. It's not quite "auto", but it's pretty cool! (side note: Is there any reason the lambda() has to be there at all? It seems a bit silly to define a lambda and then immediately evaluate it. Shouldn't the let ideally be able to suffice here?) * What is your evaluation of the implementation? I didn't look at the code enough to have an intellegent opinion on this. Everything I did seemed to work as advertized though, so at least it's relatively bug-free. * What is your evaluation of the documentation? The documentation isn't great. I don't think it's as terrible as some have made out, but there were numerous times I had questions and was unable to find an answer. One of the questions I had did have a solution in the documentation (use make_manip()-- ick), but I didn't find it in the docs before I worked it out on my own. In some cases, I'm still not sure, for example: In the quicksort example again, at (B), there is a recursive call "qs_rec[rights]". Is this call automatically lazy? Or do I have to do something to it? Earlier versions of the code didn't have this part wrapped up in a lambda, and I was able to do thunk1(qs_rec, rights), but I couldn't figure out what I needed to do here. My hunch is that I need to do nothing, but I couldn't find anything that told me so for sure. * What is your evaluation of the potential usefulness of the library? I think FC++ would be most useful in the business logic portions of applications that contain a heavy amount of interface code to the outside world. My understanding is that FP most shines for the sort of pure-data calculations that you'd normally do in a spreadsheet, Mathematica or S-plus. Using FC++ would make it easer to write bug-free logic (once the dang thing compiles, it's amazing how it seems to just DTRT), while still tying in to a C++ UI, a database, network services and whatever else is easier to do in an imperative way, or for which bindings do not exist for functional languages. Having FC++ as part of boost would help guarantee that the transition from "normal" C++ code to functional C++ code is a smooth one-- or at least that the pitfalls are known. For myself, I look forward most to being able to use lambda library. I know this is probably the part that most overlaps with existing parts of boost, but I think the two libraries have slightly different goals. The boost::lambda library seems most useful when you need a terse little functor to drop into an STL algorithm, whereas the FC++ lambda library seems to be useful who basically see their whole function as one big, nested lambda (which is the FP viewpoint). As I mentioned, I also believe that FC++ strives more for KISS, while BLL aims more for handling the common cases automatically (for example, the overloading of "<<") * Did you try to use the library? With what compiler? Did you have any problems? I used g++ 3.3. The only problem was slow compliation times. And trying to wrap my head around what was going on :) * How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? I've been anxiously awaiting FC++'s introduction to boost since I first found out about FC++ in Brian's review request last summer/fall. I read the papers on his website at that tme and have since revisted them. I also read through the new boost docs and monkeyed around for awhile. If you pushed it all together, it'd probably be a solid couple of days. * Are you knowledgeable about the problem domain? Not really. I had a class in functional programming using SML. I discovered Haskell while listening to a presentation about Functional Images ( now at http://conal.net/papers/functional-images/ ). I then tried to read the Gentle Introduction to Haskell, and got about as far as Monads, when my head promptly exploded. I see FC++ as a good way to approach this again, while staying in familiar territory. * Do you think the library should be accepted as a Boost library? I do. There are a fair number of improvements suggested by others that I don't disagree with, and although some seem larger in scope, when taken in consideration with the overall size of the library itself, I don't think any of the problems is so large as to require a later review before acceptance.

Thanks for your comments. A few quick replies to questions: On Mon, Mar 01, 2004 at 10:10:15PM -0800, Jared Roberts wrote:
I've been generally impressed with the design. There are a few things that bother me. I dislike having to manually promote direct functoids to full functoids. This is particularly sticky if the functoid is recursive, as above. I worked around this at (A) by using a lambda variable to hold the full functoid, but I would have thought there was a better way. If there is, I didn't see it in the documentation. I also
For recursion, you can always just say "make_full1(*this)", but that's still a little big/ugly; it would be nice if you could just say "*this". Alternatively, you can define the whole algorithm inside lambda and use "letrec" to create a recursive function definition.
One thing in particular that seems to have been overlooked by some others about the lambda variables is that they allow you to do a fair amount of type inference! Looking again at the quicksort example, rather than worry about types, I just declared everything lambda and the compiler was able to figure everything out. This is true even for qs_rec, which is bound to the full-functoid version of quicksort to be used in the recursive step at (B). FC++ is able to figure all this out, even though qs_rec isn't bound when declared. It's not quite "auto", but it's pretty cool!
Indeed! I'd never really considered FC++'s lambda/let as a stop-gap solution for the proposed "auto" type inference, but it does kinda accomplish this.
(side note: Is there any reason the lambda() has to be there at all? It seems a bit silly to define a lambda and then immediately evaluate it. Shouldn't the let ideally be able to suffice here?)
Well, "let[...].in[...]" is a lambda expression, so you need some operation to transform it into its value. But yeah, wow, lambda()[ blah ]() is pretty ugly/verbose, and I could easily make just blah() (or something similar) work. Good idea.
The documentation isn't great. [...] I'm still not sure, for example: In the quicksort example again, at (B), there is a recursive call "qs_rec[rights]". Is this call automatically lazy? Or do I have to do something to it? Earlier versions of the code didn't have this part wrapped up in a lambda, and I was able to do thunk1(qs_rec, rights), but I couldn't figure out what I needed to do here. My hunch is that I need to do nothing, but I couldn't find anything that told me so for sure.
It is not lazy. (More precisely, it is not lazy with respect to the lists, which I think is what you want. It is lazy in the sense that it is a lambda expression, and thus will not get evaluated until the lambda is evaluated.) To fix it, just change qs_rec[lefts] %cat% (pivot %cons% qs_rec[rights]) to qs_rec[lefts] %cat% (pivot %cons% thunk1[qs_rec,rights]) Except, that I just tried it and this doesn't work. Doh! Here's a comment in the library implementation: // FIX THIS: the thunkNs are not full functoids. Oh well. Until I find // a need to make them so, I'm not going through the effort. So consider this a bug/oversight, and watch the library author sheepishly slink away to fix it. :) -- -Brian McNamara (lorgon@cc.gatech.edu)

Jared Roberts wrote:
To help me evaluate the design, I wrote a few quick client programs to try and get a feel for the 'language'. The most instructive of these, was a quicksort function, modelled after the example given on the haskell home page ( http://www.haskell.org/aboutHaskell.html ):
template<typename ordT> struct quicksort : public c_fun_type<list<ordT>, list<ordT> > { [snip]
Cute. Do you have some timings for this? How fast is it? -- Giovanni Bajo

On Wed, Mar 03, 2004 at 05:40:20AM +0100, Giovanni Bajo wrote:
Jared Roberts wrote:
To help me evaluate the design, I wrote a few quick client programs to try and get a feel for the 'language'. The most instructive of these, was a quicksort function, modelled after the example given on the haskell home page ( http://www.haskell.org/aboutHaskell.html ):
template<typename ordT> struct quicksort : public c_fun_type<list<ordT>, list<ordT> >
Cute. Do you have some timings for this? How fast is it?
I did not time Jared's code, but I am sure it will be very slow (compared to, say, std::sort on a std::vector). There are lots of intermediate heap-allocated data structures (lists) being created. Like any quicksort, it's O(N^2), but the constant-factor costs here are pretty high. -- -Brian McNamara (lorgon@cc.gatech.edu)

Joel Young <jdy@cs.brown.edu> writes:
-------- From: Brian McNamara <lorgon@cc.gatech.edu>
Like any quicksort, it's O(N^2), but the constant-factor costs here are
nlog(n) ?
Average case: nlog(n) Worst case: n^2 http://www.cs.virginia.edu/~luebke/cs332.fall00/lecture6/tsld009.htm introsort is reliably nlog(n) -- Dave Abrahams Boost Consulting www.boost-consulting.com

From: David Abrahams <dave@boost-consulting.com>
Joel Young <jdy@cs.brown.edu> writes:
From: Brian McNamara <lorgon@cc.gatech.edu>
Like any quicksort, it's O(N^2), but the constant-factor costs here are
nlog(n) ?
Average case: nlog(n) Worst case: n^2
My Bad. Momentary brainfart O vs whp. Randomized quicksort IIRC is nlog(n) with high probability (prob of bad case goes to zero at least as fast as 1/n). Joel
participants (6)
-
Brian McNamara
-
David Abrahams
-
Giovanni Bajo
-
Jared Roberts
-
Joel Young
-
Mat Marcus