I'm trying to implement the <a href="http://speleotrove.com/decimal">General Decimal Arithmetic</a> (henceforth GDA) specification in Mercury as a learning project.  So far it's been a lot of fun and I've been learning quite a bit (as well as getting frequently humbled by Boney and ski in #mercury as they gently suggest obvious ways to improve my code).  There is one problem I've been facing that I can't resolve, however.  Boney suggested the use of type classes to solve it, but I just can't see how they'd help.<div>
<br></div><div>The problem is this: all calculations in GDA are done in a "context".  This context specifies the precision (in decimal digits) of the calculation, the rounding strategy in use when values have to be truncated and a set of flags for deaing with various error conditions.  The resulting type declarations look like this:</div>
<div><br></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><font face="'courier new', monospace">:- type rounding_strategy</font></div></div><div><div><font face="'courier new', monospace">    ---> round_down       % mandatory strategies</font></div>
</div><div><div><font face="'courier new', monospace">       ; round_half_up</font></div></div><div><div><font face="'courier new', monospace">       ; round_half_even</font></div></div><div><div><font face="'courier new', monospace">       ; round_ceiling</font></div>
</div><div><div><font face="'courier new', monospace">       ; round_floor</font></div></div><div><div><font face="'courier new', monospace">       ; round_half_down  % optional strategies</font></div></div>
<div><div><font face="'courier new', monospace">       ; round_up</font></div></div><div><div><font face="'courier new', monospace">       ; round_05up.</font></div></div><div><div><font face="'courier new', monospace"><br>
</font></div></div><div><div><div><font face="'courier new', monospace">:- type signal</font></div></div></div><div><div><div><font face="'courier new', monospace">    ---> signal( flag         :: bool</font></div>
</div></div><div><div><div><font face="'courier new', monospace">               , trap_enabler :: bool ).</font></div></div></div><div><div><div><font face="'courier new', monospace"><br></font></div></div>
</div><div><div><div><font face="'courier new', monospace">:- type context</font></div></div></div><div><div><div><font face="'courier new', monospace">    ---> context( precision         :: integer</font></div>
</div></div><div><div><div><font face="'courier new', monospace">                , rounding          :: rounding_strategy </font></div></div></div><div><div><div><font face="'courier new', monospace">                , clamped           :: signal</font></div>
</div></div><div><div><div><font face="'courier new', monospace">                , division_by_zero  :: signal</font></div></div></div><div><div><div><font face="'courier new', monospace">                , inexact           :: signal</font></div>
</div></div><div><div><div><font face="'courier new', monospace">                , invalid_operation :: signal</font></div></div></div><div><div><div><font face="'courier new', monospace">                , overflow          :: signal</font></div>
</div></div><div><div><div><font face="'courier new', monospace">                , rounded           :: signal</font></div></div></div><div><div><div><font face="'courier new', monospace">                , subnormal         :: signal</font></div>
</div></div><div><div><div><font face="'courier new', monospace">                , underflow         :: signal ).</font></div></div></div><div><div><div><font face="'courier new', monospace"><br></font></div>
</div></div><div><div><div><font face="'courier new', monospace">:- type sign</font></div></div></div><div><div><div><font face="'courier new', monospace">    ---> positive</font></div></div></div><div>
<div><div><font face="'courier new', monospace">       ; negative.</font></div></div></div><div><div><div><font face="'courier new', monospace"><br></font></div></div></div><div><div><div><font face="'courier new', monospace">:- type decimal</font></div>
</div></div><div><div><div><font face="'courier new', monospace">    ---> number( sign        :: sign</font></div></div></div><div><div><div><font face="'courier new', monospace">               , coefficient :: integer</font></div>
</div></div><div><div><div><font face="'courier new', monospace">               , exponent    :: integer )</font></div></div></div><div><div><div><font face="'courier new', monospace">       ; infinity(sign)</font></div>
</div></div><div><div><div><font face="'courier new', monospace">       ; quiet_nan(sign)</font></div></div></div><div><div><div><font face="'courier new', monospace">       ; signalling_nan(sign).</font></div>
</div></div><div><font face="'courier new', monospace"><br></font></div><div><font face="'courier new', monospace"><div>:- type dec == decimal.</div></font></div></blockquote><div><div><br></div><div>From this point on the API is straightforward.  Some example function declarations:</div>
<div><br></div></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><div><font face="'courier new', monospace">:- func basic_default_context          = (context::out) is det.</font></div></div>
</div><div><div><div><font face="'courier new', monospace">:- func extended_default_context       = (context::out) is det.</font></div></div></div><div><div><div><font face="'courier new', monospace">:- func to_scientific_string(dec::in)  = (string::out)  is det.</font></div>
</div></div><div><div><div><font face="'courier new', monospace">:- func to_engineering_string(dec::in) = (string::out)  is det.</font></div></div></div><div><div><div><font face="'courier new', monospace">:- func to_number(string::in)          = (dec::out)     is det.</font></div>
</div></div><div><div><div><font face="'courier new', monospace">:- func abs(dec::in)                   = (dec::out)     is det.</font></div></div></div><div><div><div><font face="'courier new', monospace">:- func add(dec::in, dec::in)          = (dec::out)     is semidet.</font></div>
</div></div></blockquote><div><div><br></div><div>Here's where the problem exists.  <b>All</b> of the above functions after the first two are executed in a context that restricts precision, that dictates signalling policy and that specifies how rounding is to be applied.  If I implement, say, divide/2 (<font face="'courier new', monospace">:- func divide(dec::in, dec::in) = (dec::out) is semidet.</font>), and divide 1 by 3, it is the associated context that stops the division from going on forever.</div>
<div><br></div><div>And here's the problem: how do I associate a given calculation with a calculation context?  Just passing it in as a parameter is a non-starter because these are the implementation functions.  The exposed API will be, shockingly, "+" for add/2, "/" for divide/2, etc.  So while it would be easy to do <font face="'courier new', monospace">divide(1, 3, MyContext)</font> in code, that's not really an acceptable solution because I can't do <font face="'courier new', monospace">1 / 3 (MyContext)</font> (or whatever).  "/" (and other mathematical operations' predicates/functions) is militantly outfitted with an arity of 2.</div>
<div><br></div><div>One solution I thought of is to pass the context in whenever constructing a number.  So when I go <font face="'courier new', monospace">number(positive, 1, 0)</font> I'd have to add a fourth parameter to the constructor so it's <font face="'courier new', monospace">number(positive, 1, 0, basic_default_context)</font><font face="arial, helvetica, sans-serif"> or something equivalent.  This solves the problem of association, however it does so at the price of adding a lot of boilerplate to each number introduced into the system and into adding a lot of boilerplate for handing collisions of contexts in operations.  (Consider if one number operates under round_up rules and another operates under round_to_even.  Which one holds sway, if any?)</font></div>
<div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">What I'd really like to do is be able to associate the context with the calculations implicity, perhaps in some kind of framing block or equivalent.  Boney's suggestion of using type classes seemed plausible, but upon investigation I can't seem to get them to work that way because the type variables in the type classes are all related to the predicate/function declarations (at least by my reading) and here the only difference is down in the predicate/function implementation.  The kind of thing I'd ideally like to see in place is something like the following pseudo-Mercury:</font></div>
<div><font face="arial, helvetica, sans-serif"><br></font></div></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><font face="'courier new', monospace">…</font></div><div><font face="'courier new', monospace">using_context(MyContex) :</font></div>
</div><div><div><font face="'courier new', monospace">    That = This + 3,</font></div></div><div><div><font face="'courier new', monospace">    Result = That * TheOther.</font></div></div></blockquote><div>
<div><font face="arial, helvetica, sans-serif"><br></font></div><div><font face="arial, helvetica, sans-serif">I just can't see any way of doing this (or something equivalent to this) short of abusing DCG notation or using global state, neither of which would really be acceptable.</font></div>
<div><div><br></div>-- <br>"Perhaps people don't believe this, but throughout all of the discussions of entering China our focus has really been what's best for the Chinese people. It's not been about our revenue or profit or whatnot."<br>
--Sergey Brin, demonstrating the emptiness of the "don't be evil" mantra.<br>
</div></div>