10:39
<jschoi>
Does anyone have any background on why Math got so many functions like abs that might have gotten put in Number? Having trouble finding in the meeting notes; I think this might predate them.
11:33
<jmdyck>
The Math object goes back to ES1
12:16
<jschoi>
The Math object goes back to ES1

Right, and as far as I can tell, post-ES1 mathematical functions were subsequently added to Math rather than Number simply to be consistent with that status quo. Is that correct?

For example, in the notes, I see some old talk of making clz32 an instance method on Number.prototype, and later it got switched to a “static” method, presumably to avoid boxing primitive numbers. But I can’t really find any explicit reasoning why Math was chosen as its home instead of Number.

12:20
<Ashley Claymore>
Looks like Number.{isInteger,isSafeInteger} and Math.{sign,trunc,log10,hypot} were added in ES6
12:21
<Ashley Claymore>
Those homes seem logical, at least to me.
12:54
<jschoi>
Those homes seem logical, at least to me.

Yeah. An issue I’m going to raise during my BigInt Math presentation is that of max (and min and hypot). The issue is that if we simply extend Math.max to accept BigInts, then Math.max(…arrOfBigInts) would unexpectedly return +Infinity whenever arrOfBigInts happens to be empty—this would basically be an unexpected implicit type conversion from an array of BigInts to a Number value.

The choices I see are to either add a separate Math.bigMax function (and bigMin and bigHypot)…or add a BigInt.max method (in which case…why don’t we have Number.max—and then what about the other Math functions, do they get copied to Number and BigInt too?).

12:55
<jschoi>
So that’s why I’m wondering about the original “philosophy” behind Number versus Math. All the other Math methods can be extended for BigInts with no conversion problems. It’s just those three variadic functions…
13:14
<jmdyck>
Note that max and min weren't variadic originally, so even if they'd anticipated other numeric types, there wouldn't have been that reason to put them in Number.
13:17
<jschoi>
Yeah—I suppose I’m wondering if anyone knows the reasoning behind the original decision to separate Math and Number functions in the first place. The original philosophy behind ES1 Math.abs rather than Number.abs. Perhaps that would help inform this decision.
13:42
<Domenic>
Totally uninformed guess: Brendan was used to C's #include <math.h> as the way to get access to functions like max()/abs()/etc. So he carried that over to JS as using the Math namespace object to get access to functions like max()/abs()/etc.
15:05
<danielrosenwasser>
Totally uninformed guess: Brendan was used to C's #include <math.h> as the way to get access to functions like max()/abs()/etc. So he carried that over to JS as using the Math namespace object to get access to functions like max()/abs()/etc.

alternative guess: it's on the Math class because they were trying to script java

https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html

15:06
<danielrosenwasser>

alternative guess: it's on the Math class because they were trying to script java

https://docs.oracle.com/javase/8/docs/api/java/lang/Math.html

https://twitter.com/BrendanEich/status/1263386663679438850
15:13
<Domenic>
That makes more sense
15:50
<jschoi>

https://twitter.com/benawad/status/1263183829235269634?s=20

I wonder if a precision argument for Math.round might be worth pursuing………

15:50
<devsnek>
i like proto methods
15:51
<devsnek>
they also make stuff like "empty arguments" not an issue anymore
15:52
<bakkot>
how does a proto method make sense for max?
15:52
<devsnek>
a.max(...others)
15:52
<devsnek>
if others is empty you return a instead of Infinity
15:52
<bakkot>
... no
15:53
<devsnek>
?
15:54
<bakkot>
uhh I guess I will just say that I find that extremely aesthetically distasteful
15:54
<ryzokuken>
static methods are nice though
15:54
<ryzokuken>
Number.max(...args)
15:54
<devsnek>
static methods make you have to care about which type you're dealing with
15:55
<devsnek>
like how we do Type(x)::foo in the spec
15:55
<devsnek>
i guess you could do a.constructor.max(a, b)
15:56
<ryzokuken>
oh right
15:56
<ryzokuken>
but I do still like the idea of polymorphic functions that work on mixed lists
15:56
<ryzokuken>
so namespaced makes sense for those
15:56
<devsnek>
i mean they could still be polymorphic in cases where that makes sense
15:57
<ryzokuken>
proto functions, sure, but for static methods it might not be great to make them polymorphic?
22:12
<devsnek>
so class fields use define instead of set right
22:12
<devsnek>
so there's no way for the superclass to see that happen
22:12
<bakkot>
in general superclasses do not see things subclasses do, correct
22:13
<bakkot>
same goes for methods defined by the subclass
22:13
<devsnek>
there's a cool python library i want to mimic
22:13
<devsnek>
where you can do like
22:13
<devsnek>
class Foo(lib.Model):
  field1 = lib.Int()
  field2 = lib.String()
22:13
<devsnek>
and that builds a validator on foo that you can use on incoming requests
22:13
<devsnek>
and you can also add methods and stuff to the instance cuz its a normal class
22:14
<devsnek>
but it requires the superclass knowing the fields to build the validator
22:14
<devsnek>
i think in python that's done using metaclasses
22:14
<bakkot>
that seems like a use case for decorators, at a glance
22:15
<devsnek>
hmmm interesting
22:15
<devsnek>
where would the data from the decorators be stored
22:17
<bakkot>
wait, maybe I'm not understanding
22:17
<bakkot>
how do you use Foo after setting it up like that?
22:17
<bakkot>
or do you have a link to the library?
22:17
<devsnek>
not a public library unfortunately
22:17
<devsnek>
but you can do Foo(some raw data)
22:18
<devsnek>
and it will attempt to pull the fields from the raw data and either coerce them into something matching what you specified (Int/String/etc) or throw
22:19
<devsnek>
its nothing too magical, i'm just trying to imagine how i'd duplicate the sort of declarative schema info pattern in js
22:21
<devsnek>
i guess there could be like Foo._fields that the decorators populate, and then lib.Model can check for this._fields when its being used?
22:21
<devsnek>
or Symbol('lib.Fields') would be more proper i guess :P
22:22
<bakkot>
the current decorator proposal has a concept of metadata, which I think is intended to be used for that sort of thing?
22:22
<bakkot>
I haven't been following too closely though
22:25
<bakkot>

I think the thing I'd actually do with a decorator would be more like

@fields({ field1: lib.Int, field2: lib.String })
class Foo {
 ...
}

where @fields would replace the class with a subclass which called super and then validated and installed the fields named by the decorator

22:25
<devsnek>
this seems like it could work https://gc.gy/e5d1ed0d-50fd-4394-8cfd-4f28fe049b5f.png
22:25
<bakkot>
you can probably do something closer to the Python version where the fields are inline, it's just not how I'd write it
22:26
<devsnek>
yeah, the thing that annoys me about schema libraries in js i'm familiar with is that they're all out of line
22:48
<devsnek>
this syntax is rough lol https://gc.gy/3ef68645-496c-41e2-b2db-15eac94f8244.png
23:08
<devsnek>
can a decorator remove a field
23:09
<devsnek>
this define behavior is terrible we shouldn't have done it lol
23:15
<bakkot>
having [[Set]] would not make this task any easier, because you can't have a setter for arbitrary properties
23:15
<bakkot>
you have to use a proxy trap for that, and you can trap [[DefineOwnProperty]] just as readily as [[Set]] if you're using a proxy
23:16
<bakkot>
anyway I don't think you can have a decorator which removes a field, no; in fact I think that was basically a design constraint for it to get through committee
23:18
<devsnek>
that means the base constructor can't set the new field values after they're schema validated
23:18
<devsnek>
cuz the define overwrites it
23:19
<bakkot>
... right, base classes run before subclasses, that's how that works
23:19
<bakkot>
[[Set]] would not help you there because the base class would not know about the fields
23:19
<devsnek>
well my train of thinking was, using [[set]] would make it reasonable to not perform any operation for x; instead of doing this.x = undefined;
23:20
<devsnek>
but it doesn't matter much now
23:21
<bakkot>
also I think this works: https://gist.github.com/bakkot/71bb439da868db191467762a82e85e61
23:22
<devsnek>
ah interesting
23:22
<bakkot>
not sure if it's what you were looking for though
23:22
<devsnek>
the extra constructor certainly gets around this
23:22
<devsnek>
thanks for the idea
23:30
<bakkot>
the only annoying part is that you wouldn't be able to use the fields in C's constructor, if you were trying to do something there
23:30
<bakkot>
I don't see a way around that without requiring a tiny bit of boilerplate
23:31
<bakkot>

i.e. something like

@data
class C {
  x = data.int;
  constructor(obj) {
    data.init(obj);
    // other stuff
  }
}
23:31
<bakkot>
... actually you don't even need the decorator at that point
23:31
<bakkot>
you can do that today
23:34
<bakkot>
https://gist.github.com/bakkot/9b22363769a8d88934d08d2b597dd986