Code Monkey home page Code Monkey logo

Comments (14)

oberstet avatar oberstet commented on June 12, 2024

The difference is intentional: WAMP should be usable with languages that have this semantics (of function always having a single return type). JavaScript allows "undefined". Other languages allow multiple return values. To make this work transparently, the client libs needed to adapt that. But then the API is different depending on host language: i.e. a WAMP result with multiple values would be wrapped into array for languages lacking multi-return value, but not for other languages supporting that construct. So its unified at WAMP level.

For similar reason WAMP does not provide means to have RPCs with named arguments, only positional arguments. You can of course send a dict though.

So: not a bug, won't fix.

from wamp-proto.

ghetolay avatar ghetolay commented on June 12, 2024

Ok, but then why Call messages are not following the same logic ?

What I'm saying is all messages should follow a unified specification about arguments, one way or another.

from wamp-proto.

oberstet avatar oberstet commented on June 12, 2024

Because all host languages support "call by positional arguments".

from wamp-proto.

ghetolay avatar ghetolay commented on June 12, 2024

I'm not really understanding but I'm ok if you say so.

Also I'm using Java (function always having a single return type) and I'm more comfortable with the optional multiple arguments way.

from wamp-proto.

oberstet avatar oberstet commented on June 12, 2024

The logic is: model WAMP call request and result messages along the least common denominator of host languages:

  • only positional args, no named args
  • 0, 1, .. args
  • always 1 return value

The reasoning is: there is no point in making WAMP call request/result messages "symmetric" on the wire, if that cannot be conveniently mapped to host languages. Personally I think good protocol design should take into account the fact that the protocol isn't a goal in itself, but only together with host language integration.

from wamp-proto.

beatgammit avatar beatgammit commented on June 12, 2024

I disagree. There is a distinct difference between multiple return and returning an array, and I like to think of single return as a special case of multiple return, not the other way around. Correct support of multiple-return functions requires the client to know something about the host to determine whether an array is actually an array or multiple-returns pretending to be an array.

Clients that don't support multiple return can do the transation in whatever way works for that particular language (translate into array, tuple, whatever). It doesn't seem right to restrict a protocol because most languages expect something to be a certain way, especially when making it more general costs nothing.

As a concrete example, consider the following Go examples:

  • func DoRPC1() (string, error) - if error is null, effectively a single return
  • func DoRPC2() (string, int, error) - if error is null, effectively multiple return with different types
  • func DoRPC3() ([]string, error) - if error is null, effectively single return with array

I cannot write a library to handle any of this transparently because there's nothing in the protocol that tells me there's a difference. This seems unnecessarily messy just to make single-return languages simpler, especially since it's simpler to translate a multiple-return into an array for those languages, which can be done transparently.

I understand that Go is not officially supported, so I'll back this up with a Javascript example. When writing an async function, it's common to use a callback, which can take multiple arguments:

function doRPC(cb) {
    setTimeout(function () {
        cb(null, "hello", 5);
    }, 1000);
}

This could be very nicely modeled with CALL_RESULT taking multiple arguments. Again, for languages that only support single-return (like Java), just wrap it in a List. The only clever workaround to handle multiple-returns with a single-return protocol that I can think of is to wrap all returns in an array, which is completely unnecessary since the protocol puts the return values at the end of the protocol array.

In the same vein, CALL_ERROR can be generalized to make ErrorDetails variadic as well without losing any expressiveness. It's possible for languages like Go to return multiple errors even though it's not common (and not really idiomatic either).

Please reconsider this decision. I really don't understand the logic for restricting it to a single return value. Supporting multiple-return is backwards compatible and can be simply handled by languages that don't support it.

from wamp-proto.

oberstet avatar oberstet commented on June 12, 2024

WAMP should be language neutral, so we want to support Go as well as Java and JavaScript (and others of course).

However, regarding above issue, that ("language neutral") could mean following a

  1. "least common denominator" or
  2. "superset of all"

protocol approach at both ends of the spectrum.

Existing/conceivable languages do/could provide any combination of the following with function calls:

  1. positional arguments
  2. named arguments
  3. mixed positional and named arguments
  4. no return
  5. single return
  6. multiple (positional) returns
  7. named returns
  8. mixed positional and named returns
  9. arguments with directions (IN, OUT, INOUT)

Here is a nice language overview regarding named arguments: http://rosettacode.org/wiki/Named_parameters


"no return" (4.) is different from "single return" (5.) with null, think of undefined in JavaScript or generally languages that differentiate between procedures and functions. Python always has a single return value, either explicit or implicit (None).

I am not aware of languages supporting 7. or 8., but who knows what gets invented tomorrow? However, regarding 9., there are procedural SQL dialects like PL/SQL.

Python supports 6., but you cannot differentiate between 6. and 5. returning a tuple:

 >>> def foo1():
 ...     return (1,2)
 ...
 >>> def foo2():
 ...     return 1,2
 ...
 >>> foo1()
 (1, 2)
 >>> foo2()
 (1, 2)

Regarding your JavaScript example with multiple returns - this is how you would do that with AutobahnJS:

function cb (arg1, arg2, arg3) {
   // ...
};

session.call("http://example.com#myfun1").then(function (res) {
    cb.apply(cb, res); // res is a list [res1, res2, res3]
});

Then, if the protocol would differentiate between single and multiple returns, you'd be able to differentiate in languages supporting this, but languages without support couldn't unless wrapping in a special MultiReturn object. So if there is any application semantics associated with having a multi return vs a single return being a list, languages without multi return would have a problem. On the other hand, if there is no application semantics associated, then whats the point of differentiating at the protocol level.


WAMP follows the "keep it simple" mantra. The "least common denomiator" approach is simple.

However, I'd nevertheless be interested if we can formulate a cohesive proposal for the "superset" approach (supporting all of 1. - 9., not only 1., 5. and 6.).

from wamp-proto.

ghetolay avatar ghetolay commented on June 12, 2024

Since someone seems to agree with me and most of the discussion about this was private let's make a public recap.

So you claim to do it intentionally to ease implementation of host language, especially language with function returning 1 single value.

I'm working on the Java implementation so here is why I disagree and I think in fact it make things complicated even for this kind of language :

Wamp is a communication language it means we have to serialize and deserialize object through JSON.
Only the final user may know what's been serialize and how to deserialize it.
So when I receive a message I deserialize the first part (protocol part) and stop. Then I'm returning an object as follow to the user :

class WampArguments {
        boolean hasNext();
        Object next();
        T next(Class<T> type);
}

The user can iterate over to get all the arguments and this way he can specified which type he is expecting so we can deserialize it properly. Not all language allows parameterized function but the point stay the same.
So my call function for example return a WampArguments object :

WampArguments call(...);

And I can't imagine it returning anything else like int, boolean, String... because I can't guess what's the type of the CallResult.

Now I heard a lot about wrapping it into an array. I'm not wrapping anything.

  • For serialization the whole message is already represented as an array so I'm just adding element to it. With the actual design I'm adding an array into the message array.
  • For deserialization as I said I'm keeping the message and the final user deserialize it.

The actual design is confusing because, as beatgammit said, an array can be a single return or a multiple-return wrapped. Things should be clear at protocol level for all messages like it's done for CallMessage.

from wamp-proto.

ghetolay avatar ghetolay commented on June 12, 2024

Ok we posted at same time.

The point of my previous comment was implementation are not strictly based on language specification. We have to adapt and that's the job of an implementation. Adapt a design to a concrete working mechanism.
We already have to adapt, there is no such things as mapping a message call to a function. It's more complicated , we already use different objects to handle it.

I think a protocol should be simple (not depending on any language) and easy to use at final ends.
You seems to have more concern about implementors than final user.

With the actual design final user must adapt because of this lack. The final user must "hack" the protocol by sending multiple-returns as one.
I think user shouldn't bother about it and he should be able to add and retrieve any results he wants to.

from wamp-proto.

oberstet avatar oberstet commented on June 12, 2024

Java has no multiple returns, so I don't understand why excluding multiple returns as an explicit RPC return message does any harm to "end users" (= developer programming against a WAMP library) in this case.

Yes, when calling a RPC endpoint, the user either consumes the returned value via a generic object (as you seem to do) or the user specifies the type into which the return value is supposed to be coerced into in advance. The latter option is what AutobahnAndroid provides in addition to the first.

AutobahnAndroid is a Java implementation of WAMPv1 clients-side, and there is no problem with only having single-return, and not multiple-return.

from wamp-proto.

beatgammit avatar beatgammit commented on June 12, 2024

I don't see how allowing 0 or more return values hurts anything with single returns. It doesn't solve the problem with named returns (which I've also never heard of) but it does allow 4-6 implicitly (languages that support just 5 can do their own magic). Forcing a single return only makes 5 easy, while requiring a hack for 4 and 6.

I think the best mix of extensibility and saving bytes is an ordered list of parameters:

[ TYPE_ID_CALL, callID, procURI, [ positional_parameters:list, named_parameters:dict, modifiers:list ] ]
[ TYPE_ID_CALLRESULT, CALL_ID, [ positional_parameters:list, named_parameters:dict ] ]
  1. positional arguments: [ 2, callID, procURI, [ [...] ] ]
  2. named arguments: [ 2, callID, procURI, [ null || [], {...} ] ]
  3. mixed positional and named arguments: [ 2, callID, procURI, [ [...], {...} ] ]
  4. no return: [ 3, callID ] or [ 3, callID, procURI, null || [] ]
  5. single return: [ 3, callID, procURI, [ [x] ] ]
  6. multiple (positional) returns: [ 3, callID, procURI, [ [...] ] ]
  7. named returns: [ 3, callID, procURI, [ null || [], {...} ] ]
  8. mixed positional and named returns: [ 3, callID, procURI, [ [...], {...} ] ]
  9. arguments with directions (IN, OUT, INOUT): [ 2, callID, procURI, [ [...], {...}, [ [...], {...} ] ]

Where the new third parameters parameter represents the modifiers on the parameters. For example, for this D function:

int doStuff(in int a, out int b, inout int c)

The call would look like:

`[ 2, "737aeb1c", "doStuff", [ [1, 2, 3], {}, [ [IN, OUT, INOUT] ] ]`

If they were named parameters instead:

`[ 2, "737aeb1c", "doStuff", [ [], {a: 1, b: 2, c: 3}, [ [], {a: IN, b: OUT, c: INOUT} ] ]`

This only adds a few bytes in the simple case, but it adds a lot of expressiveness and extensibility. No polymorphism is required, and the general case is optimized.

from wamp-proto.

oberstet avatar oberstet commented on June 12, 2024

Regarding "cohesive proposal for the superset approach": I think above points into the right direction. I will try to summarize / write up above, so that we can then discuss and vote up/down 2 concrete proposals: the "least common denominator" and the "superset" approach.

With regard to IN, OUT, INOUT: not sure why you include those modifiers during the call (and hence WAMP message). Lets discuss with your concrete D example (I dont know D, so bare with me if I make syntax errors):

int doStuff(in int a, out int b, inout int c) {
   int res = a + c;
   b = 2 * a;
   c = a;
   return res;
}

When calling such function providing a = 1 and c = 2, the result will be b = 2, c = 1 and the (single) return value 3. The WAMP messages might look like

[CALL, [ [1, 2], {} ] ]
[CALL_RESULT, [ [3, 2, 1], {} ] ]

Here, the single return is always before INOUT and OUT returning parameters.

Then, there are different variants of CALL:

[CALL, [ [1], {"c": 2} ] ]
[CALL, [ [], {"a": 1, "c": 2} ] ]

and different variants of CALL_RESULT:

[CALL_RESULT, [ [3, 2], {"c": 1} ] ]
[CALL_RESULT, [ [3], {"b": 2, "c": 1} ] ]

Mmh. Not sure yet what to think about above.

In any case, I don't know D, but I do know PostgreSQL PL/pgSQL, which has one of the most flexible procedure parameter and result passing styles I am aware of. And if we in the end decide to walk the "superset" road, then IMHO we should be able to support even those wield styles of PostgreSQL.

from wamp-proto.

beatgammit avatar beatgammit commented on June 12, 2024

Good point about the access modifiers. In hindsight, I can't see how it could be useful.

Another idea, drop the array wrapper:

[CALL, [1] ]
[CALL, [1], {"c": 2} ]

[CALL_RESULT, [3, 2], {"c": 1} ]
[CALL_RESULT, [3], {"b": 2, "c": 1} ]

This seems a little less cluttered, but it has the negative side-effect that the "parameters" idea isn't isolated, which could make extending a little more difficult. For example, if some kind of meta information is needed, it would go after the parameters, requiring that both positional and named parameters be supplied, even if empty. Then, after that, if another parameter type is defined (register name, external resource, whatever), this would go after that meta information.

At the risk of being slightly uglier, I like my previous proposal. However, if this cleaner version is used, then future additions should probably go in another method type: CALL_A, CALL_RESULT_A, etc.

FWIW, I am in favor of "superset of all".

from wamp-proto.

oberstet avatar oberstet commented on June 12, 2024

The "superset of all" is now in WAMPv2: https://github.com/tavendo/WAMP/tree/master/spec

from wamp-proto.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.