-
Notifications
You must be signed in to change notification settings - Fork 208
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DRAFT FOR DESIGN: Try to sniff out the mathHelpers from the amount value #2339
Conversation
9e39e55
to
2823f5f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is draft, the only substantive comment is the incompatibility of sniffing and maintaining a semantic distinction between set and strSet. I see only three ways forward, but I may be missing other options.
- Don't sniff. Use some kind of indicator instead. If so, we already know it's a separate conversation to figure out what that indicator is. Wherever we put it for now, we'll change where it is once we have auxiliary data for Una.
- Drop strSet completely. Just use set to do the job of both. As a temporary measure until we get auxiliary data on Una, this is my favorite.
- Drop the semantic significance of strSet, but somehow arrange to use it as an internal optimization when it applies. Given that this is all just temporary anyway until we have auxiliary data on Una, I think this internal optimization route is more trouble than it is worth.
packages/ERTP/src/types.js
Outdated
* Extract and return the value. | ||
* | ||
* @property {() => Amount} getEmpty | ||
* @property {(string: mathKind) => Amount} makeEmpty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally we've made types begin upper case. Should this be MathKind
?
No. I see. We already have a program variable named MathKind
, which also follows a naming convention --- how we name static namespace objects. That's unfortunate; we need a more coherent set of conventions. We should bikeshed before this gets merged, but later.
In any case, I notice you only refer to this type but do not define it.
packages/ERTP/src/amountMath.js
Outdated
helpers !== undefined, | ||
details`unrecognized amountMathKind: ${amountMathKind}`, | ||
Array.isArray(value), | ||
details`value ${value} must be a number or an array`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an error is complaining for a transient reason, I generally like to phrase the message so users know to expect change here. Perhaps a "currently" or a "yet".
packages/ERTP/src/amountMath.js
Outdated
if (typeof value[0] !== 'string') { | ||
return setMathHelpers; | ||
} | ||
return strSetMathHelpers; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the issues with this PR hinge on this. If we sniff, then we cannot distinguish set vs strSet for empty. That's ok if strSet is only an internal optimization, that's observable only in the exposed representation. But then MATH.STRING_SET
should be removed. In this PR is not only present, it is also mentioned as a publicly visible thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If value
is []
, the code above will work but for a reason that's too clever, especially if uncommented. In JavaScript, if value
(whether array or not) has no '0'
element (yes, a string), then value[0]
is undefined
and typeof value[0]
is 'undefined'
.
Either break empty out as a separate test or at least explain this cleverness in a comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If value
is, for example, harden(['foo', {}])
this will misclassify it as a strSet when it can only be correctly treated as a set.
packages/ERTP/src/amountMath.js
Outdated
add: (leftAmount, rightAmount, brand = undefined) => { | ||
optionalBrandCheck(leftAmount, brand); | ||
optionalBrandCheck(rightAmount, brand); | ||
assert.equal(leftAmount.brand, rightAmount.brand); | ||
return noCoerceMake( | ||
sniffHelpers(leftAmount).doAdd(leftAmount.value, rightAmount.value), | ||
leftAmount.brand, | ||
); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll just use add
as an example of the issue that occurs across all the binary operators. Since you look up the helper only from the left operand, even if sniffHelpers
was accurate, this would try to add harden(['foo'])
and harden([{}])
together with strSetMathHelpers
, which would not work.
packages/ERTP/src/issuer.js
Outdated
const add = (left, right) => amountMath.add(left, right, brand); | ||
const empty = amountMath.makeEmpty(amountMathKind); | ||
const coerce = allegedAmount => amountMath.coerce(allegedAmount, brand); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice.
Since you're factoring out these others anyway, it would be surprising not to factor out subtract
as part of the pile. Also pleasant.
@@ -178,7 +183,10 @@ function makeIssuerKit( | |||
const newTotal = newPaymentBalances.reduce(add, empty); | |||
|
|||
// Invariant check | |||
assert(amountMath.isEqual(total, newTotal), 'rights were not conserved'); | |||
assert( | |||
amountMath.isEqual(total, newTotal, brand), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Less compelling but still pleasant to factor out isEqual
as well.
amountMath.isEqual(total, newTotal, brand), | |
isEqual(total, newTotal), |
const newPurseBalance = amountMath.subtract( | ||
currentBalance, | ||
amount, | ||
brand, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See next comment.
const newPurseBalance = amountMath.subtract( | |
currentBalance, | |
amount, | |
brand, | |
); | |
const newPurseBalance = subtract(currentBalance, amount); |
@@ -266,7 +275,7 @@ function makeIssuerKit( | |||
splitMany: (paymentP, amounts) => { | |||
return E.when(paymentP, srcPayment => { | |||
assertKnownPayment(srcPayment); | |||
amounts = amounts.map(amountMath.coerce); | |||
amounts = amounts.map(coerce); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice payoff for factoring out coerce
that way.
const paymentAmountB = amountMath.subtract( | ||
srcPaymentBalance, | ||
paymentAmountA, | ||
brand, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const paymentAmountB = amountMath.subtract( | |
srcPaymentBalance, | |
paymentAmountA, | |
brand, | |
); | |
const paymentAmountB = subtract(srcPaymentBalance, paymentAmountA); |
const { | ||
issuer: liquidityIssuer, | ||
amountMath: liquidityMath, | ||
} = liquidityMint.getIssuerRecord(); | ||
const { issuer: liquidityIssuer } = liquidityMint.getIssuerRecord(); | ||
let liqTokenSupply = 0; | ||
|
||
// In order to get all the brands, we must call zcf.getTerms() after | ||
// we create the liquidityIssuer | ||
const { | ||
brands, | ||
maths: { Central: centralMath, Secondary: secondaryMath }, | ||
} = zcf.getTerms(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome to see all this melt away.
I think this is the correct path forward. The internal optimization work has already been done for the most part, so I think we should take advantage of it, although we should probably add it back in slowly in a later PR once we have things functionally working. For me, the sticking point that causes me to get rid of Additionally, I think we should restrict the elements within the value arrays to be either all strings or all records/presences. We know of no use cases that have a mixed array, and it is much easier to loosen restrictions later on if we need to, than it is to add restrictions and force users to change their habits. |
This is a semantic special case non-uniformity that needs to be explained, in order to do an optimization which we can postpone. Why prohibit just strings? What about numbers? If this was well motivated, perhaps. But I don't see it. Simply dropping strSet gives us everything we need except this optimization. And it is still temporary. Let's talk about this next meeting. |
Since we decide the semantics, I don't think it's a non-uniformity to enforce that MathKind.SET can only contain presences and records. It's important for UI rendering that we can anticipate the structure of the values and that they are uniform. I've added this to the Zoe/ERTP meeting agenda. |
b6a386c
to
c032ee2
Compare
c032ee2
to
82c4b1a
Compare
Closed in favor of #2561 |
This is the alternative to #2310 which does not require
brand.mathKind