json-formula icon indicating copy to clipboard operation
json-formula copied to clipboard

Make string to number coercion more strict

Open JohnBrinkman opened this issue 1 year ago • 4 comments

The last number of changes to json-formula have been trending toward being more strict wrt parameter handling and coercions. We throw many more TypeErrors and ExecutionErrors than we used to. This has brought us to a place where there's one remaining coercion that now looks really inconsistent.

The string to number coercion will turn invalid numbers into a zero: "Parse string to a number. If the string is not a well-formed number, will return 0"

Recommendation

  • Empty strings always coerce to zero (consistent with Excel, JS)
  • Invalid number strings used with arithmetic operators with throw a TypeError. e.g. 4 * "4a"
  • Invalid number strings passed to functions that require a numeric parameter will throw a TypeError
  • Invalid number strings used in numeric comparison operations will cause the comparison to return false. e.g. 4 > "3a" is false (no error)
  • The toNumber() function will not throw an exception for malformed numbers. It will return null. This will allow authors to test strings for valid numbers (this behaviour is unchanged).
  • Currently toNumber() allows only a string|number|boolean|null parameter and so will throw a TypeError for array/object. We should also allow array and object parameters and have toNumber() return null for them

JohnBrinkman avatar Mar 27 '24 15:03 JohnBrinkman

agreed

JohnBrinkman avatar May 08 '24 02:05 JohnBrinkman

Invalid number strings used in numeric comparison operations will cause the comparison to return false. e.g. 4 > "3a" is false (no error)

This means the check (a > b || a == b) will fail meaning the a < b is true. Either we should throw an error that they are not comparable or compare them so that this particular invariant should hold if (a >=b) => true then (a < b) => false.

vdua avatar May 10 '24 08:05 vdua

Either we should throw an error that they are not comparable or compare them so that this particular invariant should hold if (a >=b) => true then (a < b) => false.

We can't allow a comparison operator throw an error. There's no precedent for that in any language I'm aware of. A bad comparison needs to be benign.

Our language rules state: "If operands are mixed types, type coercion to number is applied before performing the comparison"

This is consistent with JavaScript:

const a = 10;
const b = "abc";

console.log("a < b", a < b);
console.log("a == b", a == b);
console.log("a > b", a > b);

=>

a < b false
a == b false
a > b false

Admittedly, Excel behaves differently. When it gets different types, it appears to convert both to string. But that doesn't seem better, as the expression: "10" < 100 will return false.

JohnBrinkman avatar May 10 '24 11:05 JohnBrinkman

This is consistent with JavaScript

To be more precise it is consistent with IEEE 754, as non-number strings in comparisons with numbers now behave like NaN. For better or for worse. :)

We can't allow a comparison operator throw an error. There's no precedent for that in any language I'm aware of.

Well it is the case in Python, which is dynamically and strongly typed.

>>> 'abc' < 10
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'str' and 'int'

But because of strong types, you don't have auto string to number conversions either, so it is not fully comparable.

Eswcvlad avatar May 10 '24 12:05 Eswcvlad