3 ms·
> if you're writing a web application for example, maybe you don't really need generics much All our microservices return a { result: ... } or a { result: ...,
by dmitriid 5y ago
> if you're writing a web application for example, maybe you don't really need generics much
All our microservices return a { result: ... } or a { result: ..., nextPageToken }
We would definitely benefit from generic SingleResult<T> and PagedResult<T>.
Instead, you copy-paste the same definitions over, and over, and over, and over again for every call.
- jrockway 5y agoI think I see what you're doing. Right now, each result type implements NextPager, which returns information about how to fetch the next page. You client can implement a utility like FetchNextPage: type NextPager interface { NextPage() PageSpec } func (c *Client) FetchNextPage(ctx context.Context, current NextPager) (interface{}, error) { ... } Then for each type of paged object, you write: func (c *FooClient) FetchNextFoo(ctx context.Context, current Foo) (Foo, error) { next, err := c.client.FetchNextPage(ctx, current) ... if n, ok := next.(Foo); ok { return n, nil } return Foo{}, fmt.Errorf("unexpected type: got %T, want Foo", next) } That's annoying. But, this problem has come up before with `sql`, which has rows.Next() and rows.Scan() to iterate over arbitrary row types, and you could use that as a model: pages := client.Query(...) defer pages.Close() for pages.Next() { var foo Foo if err := pages.Scan(&foo); err != nil { ... } // do something with the page } Generics would let you enforce the type of `foo` at compile time, but it wouldn't save you many lines of code. I think you still have to write (or generate) a function like `func (c *Client) ListFoos(ctx context.Context, req ListFooRequest) (Paged[Foo], error) { ... }`. We hand-wave over that in the above example with a "..." passed to query (potentially possible if you retrive objects with a stringified query, like SQL or GraphQL), but that sounds like the hard and tedious part. Let me conclude with a recommendation for gRPC and gRPC-gateway as a bridge to clients that don't want to speak gRPC. Then you can just return a "stream Foo", and the hard work is done for you. You call stream.Next() and get a Foo object ;)
- dmitriid 5y ago> Generics would let you enforce the type of `foo` at compile time, but it wouldn't save you many lines of code. You literally showed that "for each type of paged object, you write <multiple lines of entirely unnecessary code>". Where with generics you just have a single generic function. > We hand-wave over that in the above example with The problem is: there's no hand-waving in reality.
- dmitriid 5y agoI'm traveling, so can't properly expand on what I said, but without generics the choice was: - repeat unnecessary bolierplate code for every single return type - skip types completely and use a function that accepts interface{} as a parameter and assigns whatever to that variable Neither are really acceptable in my opinion. And both will be greatly simplified and improved by generics.