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