Temporarily disable all purity for debug prints

bearophile bearophileHUGS at lycos.com
Sat Apr 16 11:52:02 PDT 2011


Walter:

> Saying it is a safe way to break purity assumes that there was no purpose to the purity.
> There is no guaranteed safe way to break purity, with or without a compiler switch.

The compiler switch I am talking about doesn't break purity. Its purpose is similar to removing the "pure" attributes from the source code. And doing it is usually safe, if you do it in the whole program.

If you take a D2 program, you remove all its pure attributes and you compile it again, the result is generally a program just as correct as before. The compiler just doesn't perform the optimizations that purity allows: a function that's now not pure gets computed each time you call it, it doesn't use the pure GC Don was talking about, the (future) conditionally pure higher order functions just return a not pure result, it keeps not using global mutable state because you have not modified the source code in any other way. 

If you recompile all the program parts that use the functions that are now not pure, the only things that break are static constraints (or static asserts) that require a function pointer to be pure, and similar things, but I have not used such things so far, they are probably quite uncommon.

An example, I have a little buggy strongly pure function sqr that's pure. The smart compiler moves the call to sqr out of the foreach loop because sqr is pure and x doesn't change inside the loop:

import std.stdio: writeln;

pure int sqr(const int x) {
  int result = x * x * x; // bug
  version (mydebug) writeln(result);
  return result;
}

void main() {
  int x = 10;
  int total;
  foreach (i; 0 .. 10)
    total += sqr(x);
  writeln(total);
}

To debug sqr I use the -nopure switch, its purpose is just to virtually comment out all the "pure" annotations. I also compile with -version=mydebug (I have avoided debug{} itself just to avoid confusion):


import std.stdio: writeln;

/*pure*/ int sqr(const int x) {
  int result = x * x * x; // bug
  version (mydebug) writeln(result);
  return result;
}

void main() {
  int x = 10;
  int total;
  foreach (i; 0 .. 10)
    total += sqr(x);
  writeln(total);
}

Now the writeln() inside sqr works and its purity is not broken, it's just absent, and the compiler just calls sqr ten times because sqr is not pure any more.


On the other hand if you have a program like this:

import std.stdio, std.traits;

pure int sqr(const int x) {
  int result = x * x * x; // bug
  version (mydebug) writeln(result);
  return result;
}

auto pureApply(TF, T)(TF f, T x)
if (functionAttributes!f & FunctionAttribute.PURE) {
    return f(x);
}

void main() {
  int x = 10;
  writeln(pureApply(&sqr, x));
}

If you compile this program with -nopure it breaks, because pureApply() requires f to be pure. I think this kind of code is uncommon.

Bye,
bearophile


More information about the Digitalmars-d mailing list