Why is to(T) pure but roundTo(T) impure?

Jonathan M Davis jmdavisProg at gmx.com
Sat Jun 9 12:33:44 PDT 2012


On Saturday, June 09, 2012 20:43:42 Chris Saunders wrote:
> Hi --
> 
> I've been trying to learn more about D's purity features after
> reading David Nadlinger's interesting post on this topic. While
> 'purifying' some existing code I discovered that I can't use
> roundTo in a pure function, and I don't understand why. Is this a
> general problem with most floating-point library calls? (e.g. I
> noticed std.math.floor() is impure as well).
> 
> """
> import std.conv;
> 
> int func(double d) pure {  // compiles...
> 	return to!int(d);
> }
> 
> int func2(double d) pure {  //doesn't compile!?!
> 	return roundTo!int(d);
> }
> """
> 
> Thanks for any pointers!

One of them ends up calling an impure function and the other doesn't. All it 
takes is using one low-level function which isn't pure yet, and _boom_, it 
can't be pure. This currently happens with pretty much any and all string 
conversions, for instance, primarily because the low-level array stuff (like 
Appender) can't be pure yet.

In the case of to!int, this overload is called

------------
T toImpl(T, S)(S value)
    if (!isImplicitlyConvertible!(S, T) &&
        (isNumeric!S || isSomeChar!S) &&
        (isNumeric!T || isSomeChar!T))
{
    enum sSmallest = mostNegative!S;
    enum tSmallest = mostNegative!T;
    static if (sSmallest < 0)
    {
        // possible underflow converting from a signed
        static if (tSmallest == 0)
        {
            immutable good = value >= 0;
        }
        else
        {
            static assert(tSmallest < 0);
            immutable good = value >= tSmallest;
        }
        if (!good)
            throw new ConvOverflowException("Conversion negative overflow");
    }
    static if (S.max > T.max)
    {
        // possible overflow
        if (value > T.max)
            throw new ConvOverflowException("Conversion positive overflow");
    }
    return cast(T) value;
}
------------

Notice the lack of functions being called. The only one is 
ConvOverflowException's constructor, but apparently that works in a pure 
function (even though the constructor isn't marked as pure - maybe it's 
because it's an exception which as being thrown).

Whereas, this is roundTo's definition

------------
template roundTo(Target)
{
    Target roundTo(Source)(Source value)
    {
        static assert(isFloatingPoint!Source);
        static assert(isIntegral!Target);
        return to!Target(trunc(value + (value < 0 ? -0.5L : 0.5L)));
    }
}
------------

Note the call to std.math.trunc. It isn't pure, so voila, roundTo can't be 
pure. Now, it looks like trunc can't be pure because it's calling a C 
function, and there's a good chance that the declaration for that C function 
(core.stdc.math.truncl) can be marked as pure, and then trunc could be marked 
as pure, and then roundTo could be pure, but that obviously hasn't happened 
yet.

In general, it takes very little for a function to be unable to be pure, 
especially if it involves low level stuff and/or C stuff in any way.

- Jonathan M Davis


More information about the Digitalmars-d-learn mailing list