Writing functional code in C++ V – Miscellaneous and conclusions - Luca Bolognese

Writing functional code in C++ V – Miscellaneous and conclusions

Luca -

☕ 4 min. read

Just a cou­ple of triv­i­al­i­ties and my part­ing thoughts.

Nested func­tions

If your lan­guage has lamb­das, you don’t need nested func­tions sup­port be­cause you can im­ple­ment them us­ing it.

I am a heavy user of nested func­tions, but I’m of two minds about it. On one side, I like that they sit close to where they are used, avoid­ing go­ing out­side the main func­tion body to un­der­stand them. I also like that you don’t need to pass a lot of pa­ra­me­ters to them, as they cap­ture the func­tion lo­cals. On the other side, they end up cre­at­ing the im­pres­sion that your func­tions are very long and so, in my eyes, they oc­ca­sion­ally re­duce read­abil­ity. The IDE helps you out there (at least VS 11) by al­low­ing you to col­lapse the lamb­das.

An ex­am­ple of triv­ial case is be­low:

BOOST_AUTO_TEST_CASE(NestedFunctions)
{
    int x = 3;
    auto sumX = [&] (int y) { return x + y;};
    BOOST_CHECK_EQUAL(sumX(2), 3+2);
}

And here is a more re­al­is­tic one (not writ­ten in a func­tional style), where read­abil­ity is re­duced by the three nested func­tions (among many other things):

bool condor(
            boost::gregorian::date now,  // date to evaluate
            double spot,                 // spot price underlying
            double v,                    // ATM volatility
            double r,                    // risk free rate
            double step,                 // % of spot price to keep as distance between wings
            int minCallShortDist,        // min distance from the short call strike in steps
            int minPutShortDist,         // min distance from the short put strike in steps
            int minDays,                 // min number of days to expiry
            int maxDays,                 // max number of days to expiry
            double maxDelta,             // max acceptable delta value for shorts in steps
            double minPremium,           // min accepted premium as % of step
            Condor& ret                  // return value
            )
{
    // convert params to dollar signs
    auto stepPr            = round(step * spot);
    auto toUSD             = [stepPr] (double x) { return round(stepPr * x);};
    auto minCpr            = toUSD( minCallShortDist );
    auto minPpr            = toUSD( minPutShortDist );
    auto premiumPr         = toUSD( minPremium );
    // calc strike values for short legs
    auto atm               = round(spot / stepPr) * (long) stepPr;
    auto callShort         = atm + minCpr;
    auto putShort          = atm - minPpr;
    auto addDays           = [](boost::gregorian::date d, int dys) -> boost::gregorian::date {
        using namespace boost::gregorian;
        auto toAdd         = days(dys);
        auto dTarget       = d + toAdd;
        return  dTarget;
    };
    // calc min & max allowed expiry dates
    auto minDate           = addDays(now, minDays);
    auto maxDate           = addDays(now, maxDays);
    auto expiry            = calcExpiry(now, 0);
    // find first good expiry
    while(expiry < minDate)
        expiry          = calcExpiry(expiry, +1);
    Greeks g;
    auto scholes           = [=, &g] (double strike, int days, bool CorP) {
        return blackScholesEuro(spot, strike, days, CorP, v, r, g, true);
    };
    // find a condor that works at this expiry
    auto findCondor        = [=, &g, &ret] (int days) -> bool {
        ret.shortCallStrike                = callShort;
        ret.shortPutStrike                 = putShort;
        auto shCallPremium                 = 0.0;
        auto shPutPremium                  = 0.0;
        // find short call strike price < maxDelta
        while(true) {
            shCallPremium                  = scholes(ret.shortCallStrike, days, true);
            if(g.delta <= maxDelta)
                break;
            else
                ret.shortCallStrike        += stepPr;
        }
        // find short put strike price < maxDelta
        while(true) {
            shPutPremium                   = scholes(ret.shortPutStrike, days, false);
            if( (- g.delta) <= maxDelta)
                break;
            else
                ret.shortPutStrike         -= stepPr;
        }
        // check premium is adeguate
        ret.longCallStrike                = ret.shortCallStrike + stepPr;
        ret.longPutStrike                 = ret.shortPutStrike  - stepPr;
        auto lgCall                       = scholes(ret.longCallStrike, days, true);
        auto lgPut                        = scholes(ret.longPutStrike,  days, false);
        ret.netPremium                    = shCallPremium + shPutPremium - lgCall - lgPut;
        return ret.netPremium > premiumPr;
    };
    // increases the expiry until it finds a condor or the expiry is too far out
    while (expiry < maxDate) {
        auto days        = (expiry - now).days();
        if(findCondor(days)) {
            ret.year     = expiry.year();
            ret.month    = expiry.month();
            ret.day      = expiry.day();
            return true;
        }
        expiry           = calcExpiry(expiry, +1);
    }
    return false;
}

That is quite a mouth­ful, is­n’t it? But re­ally the func­tion is not that long. It is all these nested func­tions that makes it seems so.

Tuples

Tuples are an­other fea­ture to­ward which I have mixed feel­ings. They are re­ally use­ful to re­turn mul­ti­ple re­sults from a func­tion and for rapid pro­to­typ­ing of con­cepts. But they are easy to abuse. Creating Records is al­most al­ways a bet­ter, safer way to craft your code, al­beit more ver­bose.

The stan­dard C++ has an ex­cel­lent tu­ple li­brary that makes work­ing with them al­most as easy as in main­stream func­tional lan­guages. The be­low func­tion shows cre­at­ing them, get­ting their value, pass­ing them to func­tions and de­con­struct­ing them:

BOOST_AUTO_TEST_CASE(Tuples)
{
    auto t = make_tuple("bob", "john", 3, 2.3);
    BOOST_CHECK_EQUAL(get<0>(t), "bob");
    BOOST_CHECK_EQUAL(get<2>(t), 3);
    // yep, compiler error
    //auto k = get<10>(t);
    auto t2(t);
    BOOST_CHECK(t2 == t);
    // passing as argument, returning it
    auto f = [] (tuple<string, string, int, double> t) { return t;};
    auto t1 = f(t);
    BOOST_CHECK(t == t1);
    // automatic deconstruction
    string s1; string s2; int i; double d;
    std::tie(s1, s2, i, d) = f(t);
    BOOST_CHECK_EQUAL(s1, "bob");
    // partial reconstruction
    string s11;
    std::tie(s11, ignore, ignore, ignore) = f(t);
    BOOST_CHECK_EQUAL(s11, "bob");
}

Conclusion

I’m sure there are some fun­da­men­tal func­tional fea­tures that I haven’t touched on (i.e. cur­ry­ing, more pow­er­ful func­tion com­po­si­tion, etc…).  Despite that, I think we have enough ma­te­r­ial here to start draw­ing some con­clu­sions. To start with, where is C++ de­fi­cient com­pared to main­stream func­tional lan­guages for the sake of writ­ing func­tional code ?

  • Compiler er­rors: if you make a well na­tured er­ror, you of­ten get back from the com­piler a long page of rub­bish if you are us­ing tem­plates (and you are). You put your gog­gles on and start swim­ming through it to find the nugget of gold that is the root cause of your prob­lem. 
    I can hear you C++ gu­rus scream­ing: come on, come on, it’s not that bad. After a while you get used to it. You be­come pro­fi­cient in rub­bish swim­ming. That makes me think of the story of that guy who lived in a strange neigh­bour­hood where, as soon as you open the door of you house, some­one kicks you in the nuts. His wife sug­gested that they moved to a bet­ter part of town, but the man replied: no wor­ries, I got used to it”.
  • Syntax odd­ity: we did our best to make the gods of syn­tax happy in this se­ries of ar­ti­cles, but we are still far re­moved from Haskell, F#, etc beauty…
  • Advanced fea­tures: if you go be­yond the ba­sics of func­tional lan­guages, there is a plethora of in­ter­est­ing fea­tures that just make your life so much eas­ier and bring your code to an higher level of ab­strac­tion. Things like mon­ads (i.e. f# async work­flow), type providers, Haskell’s lazy ex­e­cu­tion, Haskell’s su­per strong type sys­tem, Closure’s trans­ac­tional mem­ory etc… Boost is try­ing to bring many of these things to C++, but there is still a big gap to be filled.
  • Temptation to cheat: you can cheat in many func­tional pro­gram­ming lan­guages (i.e. hav­ing mu­ta­ble vari­ables), but nowhere the temp­ta­tion is as strong as in C++. You need an iron dis­ci­pline not to do it. The fact that some­times cheat­ing is the right thing to do makes it even worse.

Overall, am I go­ing to use C++ now as my main pro­gram­ming lan­guage? Not re­ally. I don’t have one of those. My gen­eral view is to al­ways use the best lan­guage for the job at hand (yes, I even use Javascript for web pages).

C++ has some unique char­ac­ter­is­tics (i.e. speed, cross com­pi­la­tion & sim­ple C in­ter­fac­ing). I’m go­ing to use it when­ever I need these char­ac­ter­is­tics, but now I’ll be a hap­pier user know­ing that I can write de­cent func­tional code in it.

0 Webmentions

These are webmentions via the IndieWeb and webmention.io.