Announcing Validity version 0.9.0.0: Validity of Double

The Validity instance of Double and Float have been the most frequently modified and most controversial part of the validity library. This post announces validity-0.9.0.0 and discusses the changes.

History

The following is a concise summary of the history of Validity Double for reference.

  • Version 0.1.0.0 until 0.3.0.0 had no Validity instance for Double
  • Version 0.4.0.0 until 0.6.0.0 had an instance for Validity where -Infinity, +Infinity and NaN are not considered valid.
  • Version 0.7.0.0 had an instance for Validity where -Infinity, +Infinity, NaN and -0 are not considered valid.
  • Version 0.8.0.0 had an instance for Validity where -Infinity, +Infinity and NaN are not considered valid.
  • Version 0.9.0.0 has an instance for Validity where any double is trivially valid.

Controversy

The Validity instance for Double has been highly controversial. The debate centers around whether NaN should be considered a valid value of type Double. The reason that this matters is because Eq Double exist and not such that == is an equivalence relationship. On a related note: Ord Double is similarly broken.

The reasons that led to the decision to consider NaN a valid value are saved in the repository as well, and can be found here.

The main trade-offs are as follows:

Consider NaN valid

  • This makes sense if you consider Double the implementation of the IEEE 754 Double precision floating point standard.
  • Round-trip properties involving Double do not hold any more. Indeed, when encoding NaN and (correctly) decoding it to NaN again, the result will not equal the starting point because NaN == NaN evaluates to False.
  • Parsers can now be tested with producesValidsOnValids in the context of Double where they couldn't be otherwise. Indeed, decoding a NaN value could be correct.
  • You can still consider a NaN invalid if it occurs as part of a custom type.

Consider NaN invalid

  • This makes sense if you consider Double as a type of rational numbers.
  • This makes sense if you see NaN as an unhandled error instead of a value.
  • This hides broken code: Eq Double and Ord Double when testing for valid values, for better or for worse.
  • Would avoid boilerplate if one's own code does not deal with NaN values.
  • You can still use genUnchecked to test code with NaN values if you want.

Breakage

Version 0.9.0.0 is not backward compatible with 0.8.0.0. It will most likely cause failing tests where those tests would not previously fail. In some cases those new failures are previously undiscovered bugs. (In particular, the following bugs were found in hashable, binary and aeson as a result of this change.) In other cases they are false positives that you need not care about. In the case of false positives, you should now add invariants to your data types that specify that the contained Double should not be NaN. Convenience functions have been added to make that easier: validateNotNaN and validateNotInfinite.

If you liked this blog post, please consider becoming a supporter:

Become A Supporter