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

Chris Saunders sencha at gmail.com
Sat Jun 9 19:06:03 PDT 2012


On Saturday, 9 June 2012 at 19:33:55 UTC, Jonathan M Davis wrote:
> 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

Thanks Jonathan. Sounds like a practical issue rather than some 
theoretical problem -- good to know.


More information about the Digitalmars-d-learn mailing list