
Boost : 
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 20080606 15:48:15
On Fri, Jun 6, 2008 at 1:29 PM, Giovanni Piero Deretta
<gpderetta_at_[hidden]> wrote:
> On Fri, Jun 6, 2008 at 6:16 PM, Daniel Walker <daniel.j.walker_at_[hidden]> wrote:
>> Hello,
>>
>
> [...]
>
>>
>> However, one thing I noticed right away while reading about Phoenix
>> was that it borrows the term rank2 polymorphic function from FC++.
>> I'm just learning this terminology, so I'm trying to gain confidence
>> that I understand it. From briefly skimming (McNamara, 2000) in JFP,
>> I'm under the impression that a rank2 polymorphic function can only
>> have two parametric types, i.e. two template parameters. So, for
>> example, std::make_pair is rank2 polymorphic, and FC++ has functions
>> that will produce pairs when called with two different types. A rankn
>> polymorphic function can have arbitrarily many different template
>> parameters; an example would be std::make_tuple. Doesn't Phoenix
>> actually support rankn polymorphism? For example, isn't the following
>> a rank3 polymorphic function in Phoenix and couldn't you write
>> similar function objects for higher ranks?
>>
>> struct tupler_ {
>> template<class> struct result;
>>
>> template<class F, class T0, class T1, class T2>
>> struct result<F(T0,T1,T2)> {
>> typedef tuple<T0,T1,T2> type;
>> };
>> template<class T0, class T1, class T2>
>> tuple<T0,T1,T2> operator()(T0 t0, T1 t1, T2 t2) const
>> {
>> return make_tuple(t0,t1,t2);
>> }
>> };
>> phoenix::function<tupler_> tupler;
>>
>
> For what I understand, rankn polymorphism has nothing to do with the
> number of type parameters but It has to do on the level of "higher
> order nesting" that still allows polymorphic functions to be used
> polymorphically. If the previous sentence didn't make sense to you (I
> would be surprised if it did :) ), I'll try with an example (I'll use
> C++0x notations)
Ah yes. I see now. Thanks! I was reading the bottom of p14 and top of
p15 of http://tinyurl.com/6njpbg. I missed that part about the
function being called first, and then its result being put into a
pair.
>
<snip>
> Rank 2 polymorphism allows a function passed as a parameter to be
> treated polymorphically:
>
> template<typename F>
> auto apply_poly(F f) > decltype(std::make_pair(f(2),
> std::string("hello world"))) {
> return std::make_pair(f(2), f(std::string("hello world")));
> }
>
> auto x = apply_poly(times_two());
>
OK, yes. I think this is the important distinction for practical
purposes. Monomorphic and rank1 polymorphic functions are monomorphic
at the call site. Rankn polymorphic functions are polymorphic at the
call site. I'm just interested in using these terms for classification
purposes, but I don't want to misuse them egregiously with respect to
their formal definitions in FP. So, here's how I'd classify various
C++ function objects.
// monomorphic
struct f {
int operator()(int);
};
// rank1 polymorphic
template<class T>
struct g {
T operator()(T);
};
// rankn polymorphic
struct h {
template<class T>
T operator()(T);
};
That's reasonable, right?
> Note that you cannot write the above function using std::function, as
> it will cause its bound function to be monomorphized (modulo parameter
> conversion). A multisignature_function would help though.
Just to clarify, for my own understanding as much as anything, if you
have a rankn polymorphic function, you're already good to go.
However, if all you have is monomorphic and/or rank1 polymorphic
functions, then multisignature_funciton would be very helpful.
This function overload aggregator  call it multisignature_function,
overload_function (I'm starting to prefer overload_function to
overload_set, now. ;) ), or whatever  would help to aggregate
monomorphic and/or rank1 polymorphic functions into a single function
object that can be used with different argument types, so long as
those types are known upfront (i.e. there's not the usual template
argument type deduction at the call site  just overload resolution).
If you can additionally include rankn polymorphic functions in the
aggregate (and use them polymorphically at the call site with argument
type deduction), then that offers more flexibility in the sorts of
aggregates you can define.
>
> Rank3 polymorphism allows passing polymorphic functions as parameters
> and still threat them as higher order rank2 polymorphic functions
> (i.e. they can in turn take a polymorphic function as parameter). You
> can guess what Rank4 and more means.
I'm actually having trouble thinking of a practical example of rank3
usage of a function object. I think it suffices to say the language
allows rankn for templated operator(), though only rank2 is usual
required, at least in the common case.
>
> Modern functional languages (Haskel, ML dialects) allow rank2
> polymorphism (but, I think, not more) at the cost of requiring some
> limited amount of type annotations.
>
> C++ allows Rankn polymorphism (i.e. there is no limit on the amount
> of nesting). The price you pay is no separate compilation (modulo
> export? but it will probably require some amount of link time
> compilation), type checking delayed at instantiation time, and of
> course the amount of type inference that can be done by templates is
> limited. And yes, both boost.MPL, FC++ and Phoenix actually support
> rankn polymorphism.
>
> Now, my understanding of all this stuff is spotty at best, so there is
> some FP expert is around here, it will very likely correct my
> explanation
No, this was all very good. I think it helps to get us all on the same
page. We don't need to wade too deeply into the minutia of FP language
design and whatnot; I'm not interested in drowning! ... only fishing
out the good bits. ;)
Daniel Walker
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk