Skip to content

Differ between a compiled Indexand an IndexBuilder #109

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

Open
smyrman opened this issue Jul 8, 2017 · 4 comments
Open

Differ between a compiled Indexand an IndexBuilder #109

smyrman opened this issue Jul 8, 2017 · 4 comments
Labels
enhancement proposal A suggestion for change that has not been accepted

Comments

@smyrman
Copy link
Collaborator

smyrman commented Jul 8, 2017

EDIT: The orginal issue was to return a concrete type from NewIndex. The issue is now about differing between an IndexBuilder and a compiled Index which serve different usage patterns and requirements. See comments for more details


Original idea

@rs, I am raising this as a potential enchantment only.

It appears to be an idiom in Go to return concrete types, and accept interfaces. resource.NewIndex() is breaking this idiom. I see the following benefits for correcting it:

  • index.Compile() would no longer be a "hidden" function, so user would not have to type-cast the returned index to a Compiler instance if using the returned Index without the rest handlers.
  • The documentation in editors (i.e. with go-to-definitions and auto-complete) would be able to display help-text when calling methods on the returned index.

These are not huge benefits, but if there is no obvious downside to the change, except for a limited change to the public API, it might be worth-while. There are also some consequences that could come out of how we fix this that we need to consider.

  • resource.Resource implements the Index interface. Could/should Bind accept a version of the Index interface? (probably not)
  • Where the Index interface is used, du we require all of the functions, or generally just the Getter functions? Should the interface be split?

Background: General information on the idiom

Some blogs on the subject:

TL;DR with some added personal experience:

Generally, returning the concrete type makes sense because:

  • When we write a function, we always know what type it will return (when we don't, such as for errors, returning an interface still make sense).
  • When we return the concrete type, users can access all public functions on the returned type.
  • Documentation in auto-complete and go-to-definition is generally better.

Likewise, it makes sense to accept interfaces because:

  • We allow for a wider usage of the API, as users may provide their own implementations, or wrapped implementations.
  • We achieve better documentation, since the function declaration itself can document which methods we are interested in using on the passed in type.
  • We allow for mocking during testing.

Sometimes these points doesn't match with our goals, and then it's probably correct to break the idiom.

@rs
Copy link
Owner

rs commented Jul 9, 2017

+1

@smyrman
Copy link
Collaborator Author

smyrman commented Mar 5, 2018

I have been thinking a little bit about this for a while, and I have just come to a perhaps slightly unexpected conclusion.

I think that an uncompiled Index and a compiled Index is not really the same thing, and does not have the same interface. I will collaborate a little-bit on this...

An uncompiled Index has the following use-case and properties:

  • You WANT to be able to bind new resources against it.
  • You WANT to eventually compile it.
  • You DON'T want to be fetching any resources from it (except for during compilation, but that's internal, not external).
  • You DON'T want to be fetching any actual resources items from it.

A compiled Index has the following use-cases and properties:

  • You DON'T want to be able to bind new resources against it (you want it to be immutable).
  • You DON'T want to compile it (again).
  • You WANT to fetch resources from it.
  • You WANT to fetch actual resources items from it.

The same can in principal be said about Resource and perhaps even Scheam and FieldValidator implementations, but we can set a line on how far we want to drag this to not create to much work to ourselves. For this issue at least, let's limit it to only touch the resource package.

Following from this, we could imagine we have the following types & interfaces:

  • type IndexBuilder struct: An uncompiled resource index where you can bind resources and call Compile to return a new compiled index (as a concrete type that implements the Index interface). No interface is needed for IndexBuilder.
  • type Index interface: A compiled index only allows resource lookup, no binding or compiling.

Optionally, we do the same for Resource, and define a ResourceBuilder type that contains the Bind and Compile methods. Either way, removing Bind from the Index inteface allows the Resource type to fully implement the Index interface, which could be very handy.

@smyrman
Copy link
Collaborator Author

smyrman commented Mar 5, 2018

PS! This design also completely guards against users using an uncompiled index by mistake, as doing so would now be a compiler error.

This of course also eases the documentation, as we no longer need to put warnings in the README alá:

if you use index without using rest.NewHandler or grahql.NewHandler, you must remember to Compile the index yourself.

@rs
Copy link
Owner

rs commented Mar 5, 2018

I like the idea.

@smyrman smyrman changed the title Should resource.NewIndex return a concrete type? Differ between a compiled Indexand an IndexBuilder Aug 2, 2018
@smyrman smyrman added the proposal A suggestion for change that has not been accepted label Sep 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement proposal A suggestion for change that has not been accepted
Projects
None yet
Development

No branches or pull requests

2 participants