-
Notifications
You must be signed in to change notification settings - Fork 28
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
Subscription and useSubscription API discussion. #72
Comments
I wrote an extensive explanation on how GADT can solve the problem with we go with 1 hook / 1 component, I believe it can't be further simplified, but all that complexity will only live inside the For future readers without the complete context: /* handler is a function that receive previous subscription data, current data and return an accumulator of those. The accumulator can be any data structure of the user choosing. */
type handler = (option('acc), 'resp) => 'acc;
/* request represents a graphql request that returns a data with type 'a*/
type request('resp);
/* This would be useSubscription hook type definition, in pseudo-reason */
type useSubscription = (~handler: handler('acc, 'resp)=?, request('resp)) => ?; So, the question is: which type should go on GADTTaking the example from the link above, how can we write a function that given the value of type prim =
| Int(int)
| Float(float)
| String(string)
| Bool(bool); If we try to write it like: /*
* What's the signature here?
* let prim: prim => ?
*/
let prim =
fun
| Int(i) => i
| Float(f) => f
| String(s) => s
| Bool(b) => b; It won't compile with the following error: To make it compile, we have to encode more information in our type, this can be done using what's called Phantom Type Using a phantom type we define prim as: type prim('a) =
| Int(int): prim(int)
| Float(float): prim(float)
| String(sring): prim(string)
| Bool(bool): prim(bool); Here we defined a phaton type let eval: type a. prim(a) => a =
fun
| Int(i) => i
| Float(f) => f
| String(s) => s
| Bool(b) => b; Here we use the phantom type in Using GADT for useSubscriptionTo achieve what we want with GADT, a proof of concept (without interop) could be defined as follow: /* Equivalent to UrqlTypes.request */
type request('resp) =
| Box('resp);
/*
* A new definition of UrqlTypes.handler
* 'ret here is our phantom type
* 'ret is used to define our return type in useSubscription
* When we have a Handle, 'ret is given the concrete type 'acc
* When we have a NoHandle, 'ret is given the concrete type 'resp
*/
type handler('acc, 'resp, 'ret) =
| Handle((option('acc), 'resp) => 'acc): handler(option('acc), 'resp, 'acc)
| NoHandle: handler(_, 'resp, 'resp);
/* Type definition of useSubscription hook */
type useSubscription('acc, 'resp, 'ret) =
(~handler: handler('acc, 'resp, 'ret), ~request: request('resp)) => 'ret;
/* Actual hook */
/*
* This signature reads as:
* For all types acc, resp and ret
* We receive a named argument of type handler(acc, resp, ret)
* And a request of type request(resp)
* The return is of type ret
*/
let hook =
(type acc, type resp, type ret, ~handler: handler(acc, resp, ret), ~request: request(resp)): ret => {
let Box(r) = request;
switch (handler) {
| Handle(handler_fn) => handler_fn(None, r)
| NoHandle => r
};
};
let my_handler = (acc, data) => {
switch (acc) {
| None => [data]
| Some(l) => l @ [data]
}
};
foo(~handler=NoHandle, ~request=Box(5));
/* Returns int 5 */
foo(~handler=NoHandle, ~request=Box("5"));
/* Returns string "5" */
foo(~handler=Handle(my_handler), ~request=Box(5));
/* Returns list(int) [5] */
foo(~handler=Handle(my_handler), ~request=Box("5"));
/* Returns list(string) ["5"] */ |
Closing for now, with the current approach being a 1 hook, 2 component solution 😂 We're using the GADT approach for the hook, and will port it over once the |
@Schmavery @gugahoa I'm opening this issue here as a place for us to track API ideas for
Subscription
anduseSubscription
. I checked out the implementation here: https://reasonml.github.io/en/try?rrjsx=true&reason=C4TwDgpgBAThCOBXCBnYAKA5AIwJRQF4AoKKAHygCEB7ADyzwG4ijRIoALAQwDsATADYQYWLgBooOCZgDG+YqQoAJXoIjp01MMACW1HqNzS8hAHySuuAFydVQkVt37Dx6ZZLkoAOWor+Qm25-YXQAfVdJJhY2aEQUCABlRGwUGRgdbT0DTHFI6TlCD3QAPyC1GEC7EJyI2SMoYrgkVGAbJuQ0Blx5c1lmIiFgKAAzampC0nQYqFzp7AlpmQlSqorbYJFc+ag5ZfaWtoQOjDxrHbMoAG8PUkGqOnQYeVgjluZSUhQAdx1gGQ4oOgyvZ8NcPp4-GogatQsMeD11uVYQYfDwIBInjdPD5IUILjAsQBfd5QYkeIijaglYHCAg4qp7V5oAg0egAVm6zEp1NWdN8DIa+2ZrPQACI2aLORSxjyNgRcep0FwZEsoHwuMBLBcwVBvr9-oDlQUdRRUdACOYANrqzUAXQ8FAS1AAtuoBAiBFAAAJQa0arj20iEoiE+qNJnAFkPDm4RhAA and I'll confess I don't 100% understand it, but it seems like an interesting idea. I guess the major discussions to have are:handler
?handler
that'd behave like(~prevSubscriptions as _, ~subscription) => subscription
?I'd say let's discuss things here and folks can feel free to submit PRs w/ suggested implementations. Thanks to you both so much for all these contributions too, I can really feel the lib getting better and better.
The text was updated successfully, but these errors were encountered: