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

Question: extend Elmish.Bridge to RPC with a ReplyChannel abstraction #29

Open
0x53A opened this issue Jul 17, 2020 · 7 comments
Open

Comments

@0x53A
Copy link
Contributor

0x53A commented Jul 17, 2020

If this is outside the scope of your Vision of Elmish.Bride, please feel free to just close this ;)


I like the elmish concept in general, and I already use and like Elmish.Bridge for pushing data from server to client.

It is really good for one-way, message based communication.

But often you also need a RPC style, request-response flow, and that is a little bit cumbersome with just fire-and-forget messages.


I haven't thought too deeply about it, but what would you think about adding RPC capabilities by emulating some Actor models with a ReplyChannel abstraction?

The proposed API use would look very similar to the MailboxProcessor:

You have a ReplyChannel class/interface.

On the serverside on the hub, you have AskClient / AskClientIf, similar to BroadcastClient / SendClientIf.

On the client side you have Bridge.Ask, complimentary to Bridge.Send.


IMO the Msg and update parts of elmish are already very similar to an actor, this would just extend this capability.

@Nhowka
Copy link
Owner

Nhowka commented Jul 26, 2020

I'm not sure if I understand it completely... Would it be the same as the following?

Today:

  • Client sends GiveMeValueA
  • Server receives GiveMeValueA and responds with HaveTheValue (Some A)
  • Client uses HaveTheValue (Some A) in its update function

After that:

  • Client sends GiveMeValueA and stops
  • Server receives GiveMeValueA and responds with HaveTheValue (Some A)
  • Client continue processing after the response

Is the idea make it possible to write code as the following?

let someFunction n =
  async {
    let! value = Bridge.Ask GiveMeValueA
    match value with
    | HaveTheValue (Some a) -> return somethingWith a
    | _ -> return somethingElse
  }

If that's it, I like the idea! Maybe Bridge.Ask could have the same signature as MailboxProcessor.PostAndAsyncReply? Not sure how to implement it, to be honest...

@0x53A
Copy link
Contributor Author

0x53A commented Jul 29, 2020

Yeah something similar like that.

I'd make it as similar to the MailboxProcessor as possible, so it could look like this:

// defined by Elmish,Bridge

type IReplyChannel<'T> with
    abstract member ReplyWithValue : 'T -> unit
    abstract member ReplyWithException : exn -> unit


// shared user code

type Msg =
| GiveMeValueA of rc: IReplyChannel<int>


// RPC-Client (web-server)

async {
    let! result = hub.Ask(fun rc -> GiveMeValueA rc)
    // result is 42
}


// RPC-server (browser)

let update =
  match msg with
  | GiveMeValueA rc->
      rc.ReplyWithValue(42)

of course this RPC stuff should be two-way, so that the browser could also "ask" the web-server.

Not sure how to implement it, to be honest...

You'd have to transparently track the request / response messages with some internal message ids to correlate them, then add a timeout if the other side doesn't respond after some time, etc ...

It would add a bunch of additional hidden state that must be tracked.

@Nhowka
Copy link
Owner

Nhowka commented Mar 15, 2021

I might have now the knowledge I was missing to implement this feature. I hope to have some fruitful experiments soon!

@Nhowka
Copy link
Owner

Nhowka commented Mar 17, 2021

@0x53A I added a method AskClient on the ServerHub as an experiment on the version 5.0.0-rc-4. Are you still interested in testing it?

Also, I'm not sure what to do for the cases with multiple clients. Would a function callback receiving the clientDispatcher, serverDispatcher and the returned value (maybe as Result<'T,exn>) be a good API?

I'll try to implement the reverse communication soon.

@Nhowka
Copy link
Owner

Nhowka commented Mar 19, 2021

Client now has a Bridge.AskServer to do the same in the reverse direction on rc-7!

@Nhowka
Copy link
Owner

Nhowka commented Mar 19, 2021

rc-9 changed the transport a little so the message that goes to server is now smaller.

@Nhowka
Copy link
Owner

Nhowka commented Mar 20, 2021

5.0.0-rc-9-1 now have AskAllClients and AskAllClientsIf. The latter has a predicate function on the model and then both have an IReplyChannel<'T> -> 'client to create the message to be sent, a Dispatch<'client> -> Dispatch<'server> -> 'T -> unit to process the value and send new messages if needed, and a Dispatch<'client> -> Dispatch<'server> -> exn -> unit for processing the exception. Not sure how the client send the exceptions to be honest.

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

2 participants