Brendan Eich (2013-07-28T18:23:03.000Z)
domenic at domenicdenicola.com (2013-08-12T05:16:14.316Z)
See http://www.slideshare.net/BrendanEich/value-objects (tweet: https://twitter.com/BrendanEich/status/360538755309912064), in particular slide 13: --- # typeof travails and travesties - Invariant -- these two imply each other in JS: - `typeof x == typeof y && x == y` - `x === y` - `0m == 0 && 0L == 0` means `0m == 0L` (transitivity), but `0m !== 0L` (different precision and radix) so `typeof 0m != typeof 0L` per the invariant - Usability favors `typeof 0L == "int64"` and `typeof 0m == "decimal"` anyway - Making `typeof` extensible requires a per-realm registry with throw-on-conflict --- The appendix of this post proposes how to make typeof extensible, with throw-on-conflict in general. A special case for null allows ```js Function.setTypeOf(null, "null") ``` and ```js Function.setTypeOf(null, "object") ``` to be called multiple times per realm, so new code can opt into sane typeof null. The attempt to break compatibility for ES6 proposed at http://wiki.ecmascript.org/doku.php?id=harmony:typeof_null failed by being too web-incompatible, but given `Function.setTypeOf` for value objects, it's easy to support opt-in typeof repair. I considered syntax instead of API: ```js typeof null = "null"; ``` (i.e., heretofore illegal left-hand side expressions consisting of typeof unary expressions), but this fails to express the symmetric relationship when applied to value object constructors: ```js typeof bignum = "bignum"; ``` does not match runtime sense of `(typeof bignum)`, which must return `"function"`. Instead, ```js Function.setTypeOf(bignum, "bignum") ``` works with minimal boilerplate (the `.prototype` is implicit for the first parameter) and the value-object-constructor-function-or-null leading parameter fits the Function-static namespacing. As promised, other than when passing null as the first actual parameter, `Function.setTypeOf` throws on conflict. Comments welcome. # extensible typeof proposal Per-real typeof customization API, for two use-cases: * Value objects (http://www.slideshare.net/BrendanEich/value-objects) * Opt-in typeof null fixing and unfixing. Two Map instances, internal to the specification: - ExemplarToTypeofMap: object -> string - TypeofToExemplarMap: string -> object New `Function.setTypeOf` static, a peer of `Function.defineOperator`: ```js Function.setTypeOf(V, U) ``` ``` 1. Let S = ToString(U) 2. Let T = V 3. If T is null then 3a. If S is not "null" and S is not "object", throw 4. Else 4a. If T is not a value object constructor, throw 4b. T = T.prototype 4c. If TypeofToExemplarMap.has(S), throw 5. Call TypeofToExemplarMap.set(S, T) 6. Call ExemplarToTypeofMap.set(T, S) ``` From the draft ES6 spec: ``` 11.4.3 The typeof Operator from Table 31: +-----------------------+------------------------------------------------+ | ... : ... | +-----------------------+------------------------------------------------+ | Null | ExemplarToTypeofMap.has(val) | | | ? ExemplarToTypeofMap.get(val) | | | : "object" | +-----------------------+------------------------------------------------+ | ... : ... | +-----------------------+------------------------------------------------+ | Object (value object) | ExemplarToTypeofMap.has(val) | | | ? ExemplarToTypeofMap.get(val.[[Prototype]]) | | | : "object" | +-----------------------+------------------------------------------------+ ``` Notes: * "is (not) a value object constructor" and "(is a) value object" TBD * Value objects are non-extensible so their [[Prototype]] can't change