This is a possibly useful concept where you shape your data on the fly without too much thought up front, and apply any specific processing functions you desire later.
I've been playing around with representing data in a "monadic" style, where the definitions of the data constructors are postponed indefinitely, until the moment you actually decide precisely how you want to process the data.
It's all quite compact, elegant, amazing, infinitely flexible, and all that, but I fear that programmers will need to be geniuses to do stuff like this. (See the follow-up post Ordinary Programming.)
Oh well, onward where angels fear to tread. Incidentally, this post was sparked by this discussion I had yesterday on Hacker News, where I talked about how all data in Fexl are represented as functions.
In this example, I compute the gains on a series of lots of securities purchased in various quantities and prices. These could be either realized or unrealized gains, the computation is the same.
\test_monad=
(
# First let's define a test scenario. Notice how we leave the definition
# of "close" wide open. You can pass in any function you like. We're basically
# just sketching this scenario out on a whiteboard without really knowing or
# deciding exactly what's going to be done with it.
\test_scenario =
(
\close
# Here are some lots, with quantity and open price. Notice how we leave the
# definitions of "lot" and "end" wide open. (Again, you can pass in any
# functions you like -- you'll see that done later.) Naturally this lot data
# could be the result of parsing some input data, but I just write it out
# directly as functions here.
\lots=
(
\lot\end
lot 25.0 38.0;
lot 70.0 39.0;
lot 80.0 35.6;
lot 60.0 33.2;
end
)
# Close those lots at various prices.
close 38.5 lots;
close 45.5 lots;
close 35.4 lots;
# Close some other lots at a price of 20.0.
close 20.0
(
\lot\end
lot 2.0 18.0;
lot 7.0 21.3;
lot 10.0 20.9;
end
)
)
# OK now that we've defined our test scenario, let's define some specific
# functions to process the lots in a "monadic" style.
# This function is applied to each lot.
\close_lot=(\close_price\qty\open_price \next \total_qty\total_realized
\realized=(mul qty (sub close_price open_price))
print "close_lot qty ";print qty;
print " open ";print open_price;
print " close ";print close_price;
print " realized ";print realized;nl;
\total_realized = (add total_realized realized)
\total_qty = (add total_qty qty)
next total_qty total_realized
)
# This function is applied at the end of the lots.
\finish=
(
\qty\realized
nl;
print "total qty = ";print qty; nl;
print "total realized = ";print realized; nl;
)
# This demonstrates closing some lots at a given price.
\demo_close = (\close_price\lots
print "== demo_close at ";print close_price;nl;nl;
lots (close_lot close_price) finish 0.0 0.0;
nl;
)
# Here we run the test scenario using the demo close function.
test_scenario demo_close
)
# Here we run the whole monad test.
test_monad
Here's the output:
== demo_close at 38.5 close_lot qty 25 open 38 close 38.5 realized 12.5 close_lot qty 70 open 39 close 38.5 realized -35 close_lot qty 80 open 35.6 close 38.5 realized 232 close_lot qty 60 open 33.2 close 38.5 realized 318 total qty = 235 total realized = 527.5 == demo_close at 45.5 close_lot qty 25 open 38 close 45.5 realized 187.5 close_lot qty 70 open 39 close 45.5 realized 455 close_lot qty 80 open 35.6 close 45.5 realized 792 close_lot qty 60 open 33.2 close 45.5 realized 738 total qty = 235 total realized = 2172.5 == demo_close at 35.4 close_lot qty 25 open 38 close 35.4 realized -65 close_lot qty 70 open 39 close 35.4 realized -252 close_lot qty 80 open 35.6 close 35.4 realized -16.0000000000002 close_lot qty 60 open 33.2 close 35.4 realized 132 total qty = 235 total realized = -201.000000000001 == demo_close at 20 close_lot qty 2 open 18 close 20 realized 4 close_lot qty 7 open 21.3 close 20 realized -9.1 close_lot qty 10 open 20.9 close 20 realized -8.99999999999999 total qty = 19 total realized = -14.1