Attendees:
- Shu-yu Guo (SYG)
- J. S. Choi (JSC)
- Waldemar Horwat (WH)
- Jordan Harband (JHD)
- Chengzhong Wu (CZW)
- Peter Hoddie (PHE)
- Bradford Smith (BSH)
- Philip Chimento (PFC)
JSC: Welcome everyone. I'm a physician but involved in research and informatics. This is a call for a proposal I'm championing about what we do about other math operations on BigInt. I'd like to come to overall consensus on the overall philosophical approach and some side debates. There's been some controversy and some back and forth.
In the first place these are all papercuts. I work with BigInts, sometimes with stats stuff. I ran into this with high res timers in Node. Originally Node used Numbers, and I used Math.abs (because times weren't monotonic). Node switched to BigInts, and I felt like I had to change more code than was necessary since BigInts weren't supported with Math operations. It's a papercut but it's a papercut I keep encountering. When I write new algos I have to think if BigInt is supported or not. Also when writing code when transitioning from Number to BigInts.
Two approaches: extend Math, or change to BigInt like BigInt.abs. We've been talking in issues and there have been disagreement whether we should have polymorphic Math or new BigInt functions. My mental model is that Math methods are extensions of math operators, and I understand this consistency is subjective. I'm also concerned about unnecessary code rewriting. We have to care about where we're losing division, but we don't want to rewrite. Also memorization burden when we add functionality for BigInts. I would argue either approach involves a memorization burden.
Also questions about polyfillability. There's arguments against lack of polyfillability. I personally think long term ergonomics will be hurt with monomorphic math functions.
How much should we weigh 5 year transition pain ergonomics? How should we as a committee weigh long-term design wrt polyfillability. I want to note there's been performance concerns about monkeypatching polymorphic functions and transitive dependencies. Those probably would be obviated if we used standalone implementations and discourage monkeypatching the Math methods during the transition period. We could use static imports during the transition period after like 6 years. This solution has already been deployed elsewhere in the web platform.
We're facing fundamental philosophical question: polymorphic Math or monomorphic BigInt functions. Which offers better DX? I personally think polymorphic Math functions, like the polymorphic math syntax operators. If cognitive benefits are there, are they small? How much should we prioritize 5-year transition pain? I think encouraging standalone implementations would minimize that pain.
Other debates: are BigInt sqrt and cbrt useful? min/max over mixed-type equivalents?
WH: It's true that plus, minus and such are polymorphic but even simple things like adding 2 or multiplying by 2 are not polymorphic since you have to change 2 -> 2n. It's hard to make a principled argument either way. Rather than looking at principles which would lead us to talk too much in the abstract, it matters which functions we're doing. I'd like to agree on the list of functions first and then decide if we're doing polymorphic or have a separate BigInt version.
JSC: Do you think it'd be more productive for me as champion if I propose individually like "BigInt proposal for abs"?
WH: No, the danger of individual proposals is inconsistent answers for different ones, which would confuse users. We need to do all of them together.
JSC: At the same time you want to focus on specific instances even if all together?
WH: Yes, that'll inform our answer. Suppose we decided they should be separate for functions X Y and Z, and then we come to U where it's much better to make U polymorphic. We'd end up with an inconsistent approach.
JSC: There's a list on the explainer: abs, pow, sign, min, max, and maybe sqrt, cbrt. With most of those unary functions, pow is binary, min and max would support numeric types, is there a difference between appropriateness of polymorphism vs monomorphism for those functions?
WH: Yes. Interesting ones are min and max. Very useful for min and max to have identity, identity for min is +∞, identity for max is -∞. Those are not BigInts but are Numbers, which steers us towards polymorphic.
JSC: I've also seen it suggested to add a BigInt.min and BigInt.max. Opinions?
WH: Suppose we introduce decimals, then will we have Decimal.min and max in addition to BigInt.min and max and Number.min and max? Which ones would we use when mixing BigInt and Decimal?
JSC: That would be a problem with that approach.
WH: sqrt is appropriate for supporting on BigInt. Implementation isn't bad, obvious answer.
JSC: No strong opinion on whether that's BigInt or Math?
WH: I want it in the language, if everything else is polymorphic it'd be bizarre to have sqrt be the odd one.
JSC: So you're saying the considerations for functions are different but whatever we do for min, max, abs, we should do the same for sqrt?
WH: Yes.
JHD: I think I agree heavily with everything WH said. We should do all of them at once. We don't have the authority or capability to discourage polyfills. I completely reject concerns about polyfills. If they are slow people will not choose to use them. If they do choose to use them, then the performance is worse(?) than the convenience cost. I also think with JITs userland impls of polymorphic min/max won't be meaningfully slower in most code bases but mostly in artificial benchmarks and in hot paths. If my use case is I have a few values some which are Numbers and some of which are BigInts, what's my choice?
If we do what I think is the ideal thing, which is make the Math methods polymorphic, I don't think there will be performance or polyfill concerns and I don't get them.
JSC: Most of the polyfill concerns I see come from jsbi implementers like BSH. Also to clarify you're specifically talking about monkeypatching implementations, not standalone?
JHD: Yes I'm talking about replacing the Math ones.
SYG: I’m in favor of monomorphic approach of BigInt functions, … Lack of use cases, preferring not having sqrt, cbrt.
JSC: Do you think having sqrt and cbrt separated in another proposal would get this proposal likely to pass
SYG: Yes
WH: I don’t find this proposal to be compelling without having sqrt. It’s tiny and noncontroversial to implement, useful for a fair bit of number theory math stuff I’ve done in the past, and harmless.
WH: I also want to respond to JHD's comment earlier about min/max.
JHD: I realized as I was talking probably doesn't work.
WH: I'd also note min/max if you behave differently on -0 and +0 which raises a question of what happens if do min/max of -0, 0n, and +0 in various orders.
SYG: If there is an industry practice on sqrt, please let me know and V8 can assess. It's not clear what algorithm to use atm.
JSC: To be clear a lot of my motivation here is to reduce paper cuts for commonly used Math operations. I think they would be compelling on their own. I understand your preference to keep this as one omnibus proposal. If sqrt/cbrt were punted, would you feel strongly about the lack of compellingness for reducing the paper cuts to not agree to advancing the proposal if we floated for stage 2?
WH: Yes.
JSC: To answer your min/max question about -0, the 3 models I'm showing on the slide page 12 would have different answers.
WH: The problem is the answers would be incorrect. The definitions you are proposing would be inconsistent with what Math.min/max currently do on ±0.
JSC: I see, we'll have to investigate further. WH has expressed he'd block if sqrt/cbrt were excluded since reducing papercuts isn't compelling enough. SYG expressed V8 concerned sqrt/cbrt would have unclear algorithms and no clear use cases. WH to follow up with SYG with algorithms. Please make a comment on issue #16
SYG: cbrt doesn’t seem to be a thing v8 wants to implement. Let's get a hard line on that V8 won't implement cbrt.
JSC: Would you block on lack of cbrt, or just lack of sqrt?
JHD: I don’t understand the problem. I’m trying to understand what’s the problem to cbrt.
SYG: Most of it is we don’t want to consume time on it. To be convincing must not only that it can be implementable but also it should be long term maintainable.
BSH: Another thing is whatever algorithm we choose for e.g. sqrt, and the primary use case is crypto, if we didn't choose the exact algorithm that's acceptable to them they'd end up writing their own anyways.
WH: I wasn’t talking about encryption/decryption earlier. Use cases for, say, keygen crypto are different than encryption/decryption. If you’re doing encryption/decryption, then you can’t use BigInt at all without leaking side channels since the basic BigInt operations aren’t value-independent constant time. If constant time is not an issue, which is in most of the rest of the code, then BigInts are fine and you just need the correct answer.
PFC: I was curious about opinion of V8 team. Is that assuming the absence of Decimal?
SYG: Negative sentiment for decimal. I'd like to avoid answering the hypothetical assuming Decimal exists since right now we'd like Decimal to not exist.
BSH: For the polymorphism vs monomorphism, I appreciate your putting up both sides on the slides. I appreciate the argument that language designers should care about long term ergonomics. The existing use I saw is there's existing code using Numbers and you'd like to switch to BigInts and just plug in BigInts and just have it work. But there's a reason you can't mix BigInts and numbers today, which is a serious footgun. You should know when working with code whether it is a Number or a BigInt. Not knowing is a footgun. So I don't like min/max should be polymorphic. When you're writing your code you should know which value you have.
Main argument I would make is for the sake of the reader, hints to the reader to what kind of value it is is more important than what the writer would know. If the reader can't tell if something is a BigInt or a Number, that's bad. That's the strongest argument I'd put forward to making these monomorphic, that doesn't involve polyfillability.
If you go for total purist idea for what I'm saying we'd probably have different +, - operators. That wouldn't have worked well so I'm not trying to go for that argument. Since we have this in-between state.
WH: You can mix them with comparisons.
BSH: Not sure that was a great idea…
JHD: I completely reject that it's a footgun, this is JS and that's why we have runtime checks. Many use cases like displaying in React doesn't matter where you aren't doing math.
SYG: But these are math operators we're discussing.
JHD: Abs is useful for displaying though like currency. You already have to know if the value is safe. Forcing the author to dispatch on Number of BigInt is a big burden.
WH: I understand the argument about polymorphism being somewhat confusing and not knowing what type you get out of min/max. The reason I like polymorphism for min/max is a different use case, which is having an identity element (the infinities). This is also the thing you get when you call min/max with no arguments. BigInts don't have infinities.
JSC: I've seen BigInt.min/max proposed to also accept Infinity.
WH: Which would then make them polymorphic…
SYG: Side point to JHD re: displaying: as TC39 if we only want to support BigInt to be displayed, that’s not what BigInt was designed for. We also have to reckon with Intl using strings as the pass-through format for displaying arbitrary precision numbers (including decimals). Intl.NumberFormat V3 exists, and if display is the primary use case, I don't really want to motivate addition of Math functions that lack motivating mathematical use cases by saying they also have use cases for display.