amazon-ion / ion-js

A JavaScript implementation of Amazon Ion.
http://amzn.github.io/ion-docs/
Apache License 2.0
352 stars 48 forks source link

Usage difference between ion.Decimal and ion.dom.Decimal classes? #739

Open normand1 opened 1 year ago

normand1 commented 1 year ago

I ran into an issue today trying to write to the QLDB database with a Decimal class I built like this:

const decimalNumber = new ion.Decimal('1.0');

When I tried writing this value to QLDB I received this error message: "Ion Binary: value.writeTo is not a function"

I ended up wrapping decimalNumber with ion.dom.Decimal like so:

const writableDecimal = new ion.dom.Decimal(decimalNumber);

I was then able to write this Decimal value to QLDB.

These two Decimal classes seem to have some of the same usage and functionality, but I don't think it's made very clear that there are multiple Decimal classes in the documentation from what I can see. I'm also not sure if this the correct way to handle a strict string to decimal conversion for ION values.

It might be helpful to just add a string constructor on the ion.dom.Decimal class so I wouldn't need to make multiple hops here:

const decimalNumber = new ion.Decimal('1.0');
const writableDecimal = new ion.dom.Decimal(decimalNumber);
zslayton commented 1 year ago

Sorry for the delayed reply. I agree that this should be made clearer.

Decimal and Timestamp are the two Ion scalar types for which no existing JavaScript type can be used as representation. An ion.Decimal is the numeric quantity associated with an Ion literal like 1.0, but is not capable of storing annotations.

The types in the dom module are full Ion values (an unfortunately overloaded term 😞), which is to say that they are an (annotations, value) pair. In the case of dom.Decimal, it's a set of annotations and an ion.Decimal representing the numeric quantity.

The types in dom.Decimal are also wired up to behave like JavaScript types where possible. This means that you can use dom.Decimal in places where a Number (capital N) is allowed/expected, which is not true of ion.Decimal.

Unfortunately, Value.from(jsValue) doesn't have a hinting system (See #620), so if you give it a Number it will treat it as either an Ion integer or float, never a decimal.

In your case, you could do this:

const writableDecimal = ion.load('1.0');

This is friendlier syntax, though it is slower than your example code because it has to create an Ion reader to parse the provided text.

I think we can/should modify dom.Decimal's constructor to allow a numeric or string representation so you can do this:

// From integer Number
const writableDecimal = new dom.Decimal(1.0);
// From non-integer Number
const writableDecimal = new dom.Decimal(1.5);
// From string
const writableDecimal = new dom.Decimal('1.0');

I've opened #745 to track this.