Skip to content
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

Use constructors for WoT objects #3

Closed
zolkis opened this issue Feb 27, 2017 · 14 comments
Closed

Use constructors for WoT objects #3

zolkis opened this issue Feb 27, 2017 · 14 comments

Comments

@zolkis
Copy link
Contributor

zolkis commented Feb 27, 2017

Since WoT is going to be implemented on different types of runtimes (see also #2),
and since the root [1] WoT object has no state,
arguably it would be simpler to expose the WoT object with a constructor (internally implementations could still use a singleton).
Also, for ExposedThing and ConsumedThing we could provide constructors instead of factories. That would be more aligned with ECMAScript best practices (e.g. an ExposedThing object could be created for testing purposes, shaped locally, then exposed).

Currently the interface uses factories:

interface WoT {
    Promise<void>          discover(ThingFilter filter,
                                    ThingDiscoveryCallback onfound);

    Promise<ConsumedThing> consumeDescription(object thingDescription);
    Promise<ConsumedThing> consumeDescriptionUri(DOMString thingDescriptionURI);

    Promise<ExposedThing>  createThing(DOMString name);
    Promise<ExposedThing>  createFromDescription(object thingDescription);
    Promise<ExposedThing>  createFromDescriptionUri(DOMString thingDescriptionURI);
};

It could be changed into:

[Constructor()]
interface WoT {
    Promise<void>          discover(ThingFilter filter,
                                    ThingDiscoveryCallback onfound);
};

[Constructor(DOMString name)]
[Constructor(object ThingDescription)]
[Constructor(DOMString thingDescriptionURI)]
interface ExposedThing {
  // ... 
 Promise<void> expose();  // register with the WoT network
};

[Constructor(object ThingDescription)]
[Constructor(DOMString thingDescriptionURI)]
interface ConsumedThing {
  // ... 
  Promise<void> fetch();  // retrieve/validate the Thing
};
 

[1] edited: s/Wot object/root WoT object

@draggett
Copy link
Member

and since the WoT object has no state

what makes you assume that? A binary switch has state. Perhaps you are using state in a specialised sense, if so could you please explain.

@zolkis
Copy link
Contributor Author

zolkis commented Feb 27, 2017

Misunderstanding. I meant the root WoT object. It doesn't have properties, only functions.

@knimura
Copy link
Contributor

knimura commented Feb 28, 2017

Wouldn't it be necessary to have separate interfaces for the "object creation" and "exposed"?

@zolkis
Copy link
Contributor Author

zolkis commented Feb 28, 2017

They would be separated: new creates the object (which is not bound to the underlying platform yet), it's just a JS object. Then, for ExposedThing, invoke expose() to make it a WoT Thing (register etc).
For ConsumedThing, one should obtain the object by discovery, but it would also be possible to create with new, set the relevant properties (URI etc) and then invoke fetch().

I am not saying this model is better than factories (I am also coming from the embedded end), but that

  • it would be more aligned with the Web Platform,
  • the scripting code would be more portable (e.g. instead of using navigator.wot or wot = require("wot");, one could just say wot = new WoT();),
  • it's more easy to test/play around with the objects (can create them any time from a console, then call a function on them).

@zolkis
Copy link
Contributor Author

zolkis commented Feb 28, 2017

Of course until decided, we can stay with the current way (factories). This issue is for tracking the discussion for this option. We may also seek external opinions on this.
@domenic @anssiko @tobie would you advise?
(preliminary ED is here: https://w3c.github.io/wot-scripting-api/)

@tobie
Copy link
Member

tobie commented Feb 28, 2017

+1 to making ConsumedThing and ExposedThing constructors. Arguably, WoT could be a namespace.

The pattern of having a promise-returning function create a constructible object, call its asynchronous initializing method (here fetch?) and return it, is the pattern towards which async object construction is gravitating. Whether the promise-returning function hangs-off of a namespace or an interface depends on your other requirements.

@draggett
Copy link
Member

draggett commented Feb 28, 2017

They would be separated: new creates the object (which is not bound to the underlying platform yet), it's just a JS object. Then, for ExposedThing, invoke expose() to make it a WoT Thing (register etc).

In my NodeJS app server, to publish a thing, I call a function with the name of the thing and the JSON for its description. The library then invokes a call back with the object created based upon the description, and publishes it on the server. This makes it discoverable using a URL for the given name. The application script then initialises the bindings for the thing's properties actions and events.

Note that this process is asynchronous since the new thing may depend upon other things, e.g. when this thing has other things for some of its properties. This then requires the library to access the descriptions for those things and instantiate them as objects for this thing's application script.

I would like to know if anyone other than me has implemented support for having things as the value of properties?

I think I agree with making registration with a remote registry into a separate step under the control of the application as you may not want to register all of the things in that way.

@domenic
Copy link

domenic commented Mar 2, 2017

I don't have a lot of context here, so my apologies if I get something wrong. This is all rather abstract, with talk of things instead of concrete examples. But here are my thoughts:

  • The root WoT object should not be constructible. It represents the UA's magic ability to discover things, from what I understand, similar to how the Navigator object represents the UA's magic ability to do a bunch of stuff. A namespace might be a good replacement here, if it truly has no state.

  • Never use the term "URI" in web APIs. See https://url.spec.whatwg.org/#url-apis-elsewhere. Also, follow the capitalization rules: https://w3ctag.github.io/design-principles/#casing-rules

  • Avoid constructor overloads. A true constructor should be something that directly copies the given essential data into internal fields. If there is a way to infer the essential data from some other data, then that should be a factory. So maybe ThingDescription is the essential data, and you can infer that given name or thingDescriptionURL? In which case those latter two should be factories (perhaps static factories, e.g. ExposedThing.fromName()).

  • This idea of using a builder pattern (first create an X, then call .expose() on it to turn into an X-with-UA-magic) is rather unidiomatic in JavaScript. The better way to represent something without UA magic is just a dictionary. Having the same class represent two very distinct things is not great.

So, although I am normally in favor of constructors, from what I can gather here, it seems like they are not being used to actually construct the object with all of its relevant data; they are being used as part of a builder pattern of some kind. The original design makes more sense to me from that perspective.

@zolkis
Copy link
Contributor Author

zolkis commented Mar 2, 2017

@domenic thank you for the guidelines!

zolkis added a commit to zolkis/wot-scripting-api that referenced this issue Mar 6, 2017
…finitions according to commints in w3c#3.

Signed-off-by: Zoltan Kis <zoltan.kis@intel.com>
@h0ru5
Copy link
Contributor

h0ru5 commented Mar 6, 2017

we had two main rationales for having the rather unusal design of factories that return promises is as @domenic mentioned that you normally do not expect a constuctor to fail and its synchronous.

The methods like the factory that is taking in an URI, downloading the thing description and then creating thing for it can fail for many reasons and are based on asynchronous operations.

There are the simpler versions which are now modeled in the same way for homogenity - i agree we could as well do constructors for them and also we might consider to move them to static factories on the respective classes.

The reason for the single-entry object was twofold: keeping global clean (which can also be done with a namespace as @tobie mentioned) and providing a single entry point as convenience feature (only need to remember WoT instead several classes)

@mkovatsc
Copy link
Contributor

mkovatsc commented Mar 8, 2017

@domenic

Never use the term "URI" in web APIs. See https://url.spec.whatwg.org/#url-apis-elsewhere. Also, follow the capitalization rules: https://w3ctag.github.io/design-principles/#casing-rules

I think this needs much more discussion and eventually agreement with IETF (and maybe W3C to really simplify things). For now, I see value in building on existing, international, open Internet standards and that new concepts get new names. However, it is always good to simplify things...

In an offline discussion, I would be interested in how you plan to obsolete RFCs from within the WHATWG.

@domenic
Copy link

domenic commented Mar 8, 2017

That does seem out of scope. Let's be clear though that we won't be shipping new web platform APIs that don't converge on the URL spelling.

@zolkis
Copy link
Contributor Author

zolkis commented Mar 8, 2017

Yes, I think I fixed these in #7. There are many ways on how to go forward, but I've got the point about the URL naming.

@h0ru5
Copy link
Contributor

h0ru5 commented Mar 27, 2017

Closing issue as discussed in scripting call on 27-03-2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants