Using objects for a mapping argument

# Jeffrey Yasskin (4 days ago)

Tab and Anne suggested I ask about a use of objects here.

In api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/index.bs#device-discovery defines some arguments to a function in which I want users to express a map from integers or strings to a dictionary. One of the arguments will look like {0x004C: {dataPrefix: [0x02, 0x15]}}, while the other could be {0xFEAA: {dataPrefix: [0x10]}, "battery_service": {}}.

This is processed at api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/index.bs#ref-for-dom-bluetoothlescanfilterinit-manufacturerdata-5 using explicit calls to .[[OwnPropertyKeys]]() and some other ECMAScript operations.

Does this make sense, or should I have my users express these arguments in some other way?

If this makes sense, would you like me to propose some WebIDL operations that would make it easier?

Thanks, Jeffrey

Contact us to advertise here
# Domenic Denicola (2 days ago)

From: Jeffrey Yasskin [mailto:jyasskin@google.com]

Does this make sense, or should I have my users express these arguments in some other way?

It's hard for me to judge what the best user-facing API is without some sample code, so I'll refrain from commenting on that.

However, the prose to process it is problematic in a number of ways. For example, it checks the public length property, and uses Array.prototype.map.call directly. (Whose behavior will vary if the user overrides any of: window.Array, Array.prototype, Array.prototype.map, Function.prototype.call.) This doesn't really matter anyway, as sequences are not JavaScript arrays; they are Web IDL data types. They do not have .length properties, and you cannot call map on them. And you shouldn't be using the public, overridable BluetoothUUID.getService method; use the algorithm instead.

If this makes sense, would you like me to propose some WebIDL operations that would make it easier?

The actual [[OwnPropertyKeys]] steps look pretty reasonable, and yeah, maybe factoring them out into some kind of phrase that lets you extract things in this sort of scenario makes sense. This might tie into the discussions around OpenEndedDictionary?

# Anne van Kesteren (a day ago)

On Sun, Sep 25, 2016 at 4:52 PM, Domenic Denicola d@domenic.me wrote:

The actual [[OwnPropertyKeys]] steps look pretty reasonable, and yeah, maybe factoring them out into some kind of phrase that lets you extract things in this sort of scenario makes sense. This might tie into the discussions around OpenEndedDictionary?

Yeah, given how

let x = { 23: "x", 45: "y" } x[45] x["23"]

works it seems this would be compatible with OpenEndedDictionary.

# Jeffrey Yasskin (a day ago)

On Sun, Sep 25, 2016 at 7:52 AM, Domenic Denicola d@domenic.me wrote:

From: Jeffrey Yasskin [mailto:jyasskin@google.com]

Does this make sense, or should I have my users express these arguments

in some other way? >

It's hard for me to judge what the best user-facing API is without some

sample code, so I'll refrain from commenting on that.

There's some sample code at api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/scanning.bs#example-ibeacon. The API I'm suggesting looks like:

navigator.bluetooth.requestLEScan({ filters: [{ manufacturerData: { 0x004C: { // Apple's company id. dataPrefix: new Uint8Array([ 0x02, 0x15, // iBeacon identifier. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // My beacon UUID. ]), }, }, }], options: { keepRepeatedDevices: true, } })

The more WebIDL-friendly version would look like:

navigator.bluetooth.requestLEScan({ filters: [{ manufacturerData: [{ id: 0x004C, // Apple's company id. dataPrefix: new Uint8Array([ 0x02, 0x15, // iBeacon identifier. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // My beacon UUID. ]), }], }], options: { keepRepeatedDevices: true, } })

However, the prose to process it is problematic in a number of ways. For

example, it checks the public length property, and uses Array.prototype.map.call directly. (Whose behavior will vary if the user overrides any of: window.Array, Array.prototype, Array.prototype.map, Function.prototype.call.) This doesn't really matter anyway, as sequences are not JavaScript arrays; they are Web IDL data types. They do not have .length properties, and you cannot call map on them. And you shouldn't be using the public, overridable BluetoothUUID.getService method; use the algorithm instead. >

I dealt with users overriding the builtins using a paragraph at webbluetoothcg.github.io/web-bluetooth/#terminology saying that when the spec uses a name like BluetoothUUID.getService, that has to be interpreted as referring to its original value, not anything the user has set it to. I figure it's silly to force specifications to use a parallel library that people have to learn separately from the built-in JS library.

I'm just using the array methods to map a function over a sequence. In at least one place, I forgot and just wrote a_sequence.map(). Have other specs picked a different convenient way to say that?

If this makes sense, would you like me to propose some WebIDL operations

that would make it easier? >

The actual [[OwnPropertyKeys]] steps look pretty reasonable, and yeah,

maybe factoring them out into some kind of phrase that lets you extract things in this sort of scenario makes sense. This might tie into the discussions around OpenEndedDictionary?

Is there a good bug to follow for that?

Thanks, Jeffrey

# Boris Zbarsky (a day ago)

On 9/23/16 9:23 PM, Jeffrey Yasskin wrote:

In api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/index.bs#device-discovery defines some arguments to a function in which I want users to express a map from integers or strings to a dictionary. One of the arguments will look like {0x004C: {dataPrefix: [0x02, 0x15]}}, while the other could be {0xFEAA: {dataPrefix: [0x10]}, "battery_service": {}}.

For your case, where each value has the same "shape" (in that they are all converted to the same dictionary), this is basically similar to the OpenEndedDictionary proposal, right?

This is processed at api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/index.bs#ref-for-dom-bluetoothlescanfilterinit-manufacturerdata-5 using explicit calls to .[[OwnPropertyKeys]]() and some other ECMAScript operations.

Right, so compared to the other uses of OpenEndedDictionary this has the following additional wrinkles:

1) Some constraints on the "keys" of the dictionary. This is fine; other consumers want this too; it's simple enough to express in prose.

2) The way you set up properties on canonicalizedFilter.manufacturerData is using [[Set]] instead of [[DefineOwnProperty]]. You should probably use the latter.

3) Since you're going around creating ES objects, there's the usual question of whether you want to use the global of the callee function or the global of the "this" value to do that.

Does this make sense, or should I have my users express these arguments in some other way?

In general, this makes sense. We should really get OpenEndedDictionary specced....

# Boris Zbarsky (a day ago)

On 9/26/16 2:13 PM, Jeffrey Yasskin wrote:

The more WebIDL-friendly version would look like:

They key point being whether what's passed in is an array of things which have id properties, or an id-to-value mapping.

Note that the "array of things" approach has the drawback of having to define what happens if the same id is present multiple times...

I'm just using the array methods to map a function over a sequence.

The problem is a sequence is not a JS thing at all. So you can't do that.

You can convert it back to an ES object and then do the map bit and then convert the ES bit back to a sequence. Or you can just say that you create a new sequence of whatever type getService returns, by applying the algorithm steps of getService one by one to the values in your sequence.

Note that these two approaches are NOT black-box the same, unfortunately. Specifically, they are not the same in the face of the page overriding Array.prototype[Symbol.iterator] or %ArrayIteratorPrototype%.next. I'm not sure which behavior you want here, exactly, so I can't tell you which one you should do, but I suspect you want to directly apply the getService steps to your given sequence of IDL values instead of round-tripping through ES and the iteration protocol.

In at least one place, I forgot and just wrote a_sequence.map(). Have other specs picked a different convenient way to say that?

Not to my knowledge...

It might be worth to define a map operation for IDL sequences, indeed. We'd have to be careful to ensure that it is only defined when all the types match up nicely, because it would be bypassing all the normal IDL typechecking.

# Jeffrey Yasskin (21 hours ago)

On Mon, Sep 26, 2016 at 11:41 AM, Boris Zbarsky bzbarsky@mit.edu wrote:

On 9/23/16 9:23 PM, Jeffrey Yasskin wrote: >

In api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/index.bs#device-discovery defines some arguments to a function in which I want users to express a map from integers or strings to a dictionary. One of the arguments will look like {0x004C: {dataPrefix: [0x02, 0x15]}}, while the other could be {0xFEAA: {dataPrefix: [0x10]}, "battery_service": {}}.

For your case, where each value has the same "shape" (in that they are all converted to the same dictionary), this is basically similar to the OpenEndedDictionary proposal, right?

This is processed at

api.csswg.org/bikeshed/?url=https://raw.githubusercontent.com/jyasskin/web-bluetooth-1/masked-data-prefixes/index.bs#ref-for-dom-bluetoothlescanfilterinit-manufacturerdata-5 using explicit calls to .[[OwnPropertyKeys]]() and some other ECMAScript operations.

Right, so compared to the other uses of OpenEndedDictionary this has the following additional wrinkles:

1) Some constraints on the "keys" of the dictionary. This is fine; other consumers want this too; it's simple enough to express in prose.

Yep, I'd be happy with an OpenEndedDictionary<BluetoothDataFilterInit>

that let me write "for each <var>key</var>/<var>value</var> pair in

the dictionary, do: ..." even if I had to check and convert the keys from strings myself. To help with that conversion from strings, it'd be nice to have a pointer from WebIDL to CanonicalNumericIndexString() or the right string->number conversion function if that's not it.

2) The way you set up properties on canonicalizedFilter.manufacturerData is using [[Set]] instead of [[DefineOwnProperty]]. You should probably use the latter.

Probably via CreateDataProperty(). Done.

3) Since you're going around creating ES objects, there's the usual question of whether you want to use the global of the callee function or the global of the "this" value to do that.

Yes, creating and filling in ES objects from algorithms is difficult. For dictionaries, WebIDL itself says "Let O be a new Object value created as if by the expression ({}).", which doesn't answer the global question either.

Does this make sense, or should I have my users express these arguments in some other way?

In general, this makes sense. We should really get OpenEndedDictionary specced....

Is whatwg/fetch#164 the main issue for that?

Jeffrey

# Anne van Kesteren (10 hours ago)

On Tue, Sep 27, 2016 at 12:03 AM, Jeffrey Yasskin jyasskin@chromium.org wrote:

Is whatwg/fetch#164 the main issue for that?

I think

www.w3.org/Bugs/Public/show_bug.cgi?id=20158

is the main issue for unrestricted dictionary / OpenEndedDictionary. There's also

www.w3.org/Bugs/Public/show_bug.cgi?id=26190

as a follow up of sorts.

# Domenic Denicola (5 hours ago)

From: Jeffrey Yasskin [mailto:jyasskin@google.com]

The API I'm suggesting looks like:

Your approach seems good then; we should make it work.

I dealt with users overriding the builtins using a paragraph at webbluetoothcg.github.io/web-bluetooth/#terminology saying that when the spec uses a name like BluetoothUUID.getService, that has to be interpreted as referring to its original value, not anything the user has set it to. I figure it's silly to force specifications to use a parallel library that people have to learn separately from the built-in JS library.

I think we should not do this. It's not silly to separate the internals from the author-exposed algorithms. They have very different behavior, e.g. they operate on different types, leading to the confusion we see with sequences vs. arrays.

I'm just using the array methods to map a function over a sequence. In at least one place, I forgot and just wrote a_sequence.map(). Have other specs picked a different convenient way to say that?

Arrays aren't sequences, as Boris pointed out, so this just doesn't work; the spec is incoherent as of now.

You should do something like create a new sequence, loop over the old one, and add an element to the new sequence whose value is the result of applying the algorithm to the item from the original sequence.

Want more features?

Request early access to our private beta of readable email premium.