Skip to content

samples: Add wire tutorial#357

Merged
enocom merged 7 commits intogoogle:masterfrom
enocom:wire-tutorial
Aug 25, 2018
Merged

samples: Add wire tutorial#357
enocom merged 7 commits intogoogle:masterfrom
enocom:wire-tutorial

Conversation

@enocom
Copy link
Contributor

@enocom enocom commented Aug 22, 2018

No description provided.

@googlebot googlebot added the cla: yes Google CLA has been signed! label Aug 22, 2018
@enocom enocom requested review from tbpg and zombiezen August 22, 2018 23:03
Copy link
Contributor

@zombiezen zombiezen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I really like this sample! It rounds out the last remaining principles that Go Cloud is introducing.

values][values], as well as support for [cleanup functions][cleanup]. See the
[Advanced Features][advanced] section for more.

[readme]: https://github.com/google/go-cloud/blob/master/wire/README.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetize link targets.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

finally we create an event with that greeter. With all the initialization done,
we're ready to start our event.

In the design here, we are using dependency injection. We pass in whatever each
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link to https://en.wikipedia.org/wiki/Dependency_injection or a blog post? The Wikipedia article is denser than I'd like, but it's a start.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a good discussion on StackOverflow here that's much easier to parse than that of Wikipedia. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good find. That's a much more approachable explanation.

The `Start` method holds the core of our small application: it tells the
greeter to issue a greeting and then prints that message to the screen.

Now that we have all the components of our application ready, let's wire them
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either here or earlier, the prose needs to call out that we're stepping through how a problem would be solved by a Go programmer without Wire.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

three components, so writing the initializer by hand isn't too painful. Imagine
how useful Wire is for components that are much more complex.

To show a small part of how Wire handles more complex setups, let's refactor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I really like how just adding the error bit shows that the Wire setup stays the same (more or less) and it just grows to adjust.

(And same as above: this seems like the right place to start a new section heading.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to both. I like "Grumpy."

code and makes it easy to swap out one dependency with another. The idea of
dependency injection isn't much more complicated than this.

One downside to dependency injection is the need for so many initialization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like the right place to start a new section heading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
```

Let's summarize what we have done here. First, we wrote a number of components
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And one last section heading: Conclusion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"os"
)

func main() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I were coming into this without having the background knowledge of splitting application and setup code, I would be confused as to why main is in its own file. Consider moving this into greeter.go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved declarations from greeter.go into main.go.

Let's learn to use Wire by example. The [Wire README][readme] provides thorough
documentation of the tool's usage. For readers eager to jump right in, the
[guestbook sample][guestbook] uses Wire to initialize its components. Here we
are going to build a small greeter CLI to understand how to use Wire. The
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using the term "program" here instead of "CLI". I know CLI is a fairly common acronym, but I think expanding it out helps inclusivity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same thought occurred to me, as well. Done.

# Wire Tutorial

Let's learn to use Wire by example. The [Wire README][readme] provides thorough
documentation of the tool's usage. For readers eager to jump right in, the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/jump right in/see Wire applied to a cloud server/

By being more precise, we clarify who the audience for this sample is by contrast: this is just Wire by itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well said. Done.

}
return event, nil
}
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last thing that would be nice to showcase here: Wire's error messages. It doesn't have to be a long thing, but you could instruct the reader to remove one of the arguments to wire.Build (perhaps the NewGreeter call) and show that Wire points out what is missing. Another nice one to show would be to add in a superfluous function and show that Wire tells you that it is unused.

For someone who is first using Wire, I don't want them to be afraid of error messages. Much like Go compiler error messages, the intent is not to criticize, but to aid the programmer into writing the next function. If they see this being done with intent in the sample (a controlled environment), then it's more likely that when they come across it in practice that they will know that Wire is helping them know what to do next.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. I'll add your two suggestions.

}
```

In the intializer we assign a `Message` field to `Greeter`. Now, we can use the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: initializer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Thanks!

finally we create an event with that greeter. With all the initialization done,
we're ready to start our event.

In the design here, we are using dependency injection. We pass in whatever each
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My gut understanding of DI is it's automatic and done by a framework. Maybe here we should say something like "manual dependency injection"? Reading the SO post, this is DI, it's just not what's normally referred to as DI.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your understanding matches that of many people, i.e., there is a conflation between dependency injection as a design principle and dependency injection frameworks. I'm inclined to call out that we're using the design principle and not a framework, as a qualifier.

finally we create an event with that greeter. With all the initialization done,
we're ready to start our event.

In the design here, we are using dependency injection. We pass in whatever each
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/In the design here/In this design/

?

Alternatively, "This uses manual dependency injection."

In the design here, we are using dependency injection. We pass in whatever each
component needs. This style of design lends itself to writing easily tested
code and makes it easy to swap out one dependency with another. The idea of
dependency injection isn't much more complicated than this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we need this sentence - it makes me wonder what else is there? What do I not know?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed.

passing it into the next one, we instead have a single call to `wire.Build`
passing in the initializers we want to use. In Wire, initializers are known as
"providers," functions which provide a particular type. We add a zero value for
`Event` as a return value to satisfy the compiler. This won't be included in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reading this, I might wonder, can I put default values in the Event or does it have to be the zero value? Or is it always ignored?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to call this out.


Note, a [build constraint][constraint] requires a blank, trailing line.

In Wire parlance, `InitializeEvent` is an injector. Now that we have our
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style comment: I wonder if Wire terms should be italicized, or something similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following the introduction of providers above, I have quoted a new term at its first appearance.

}
```

We have added a `Grumpy` struct field to `Greeter` and if the invocation time
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Grumpy field to the Greeter struct"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

```

We have added a `Grumpy` struct field to `Greeter` and if the invocation time
of the initializer is an even number of seconds since the epoch, we will create
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unix epoch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

thing inside the generated code: it checks the error and returns early if one
is present.

As another improvement, let's look at how Wire generates code based on the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New header?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

As another improvement, let's look at how Wire generates code based on the
signature of the injector. Presently, we have hard-coded the message inside
`NewMessage`. In practice, it's much nicer to allow callers to change that
message however they see fit. So let's change `InitializeEvent` to look like this:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a sentence like:

"Wire will pass initializer arguments with the same name to any other initializers."

Maybe be a better way to phrase it.

Copy link
Contributor Author

@enocom enocom Aug 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a sentence clarifying this point, although as I understand Wire decides what goes where based on types rather than names.

our component initializtion. As long as we tell Wire how to provide (i.e.,
initialize) a component, we may add that component anywhere in the dependency
graph and Wire will handle the rest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention when the generation should be done? Should the generated code be checked into git?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a good point. Done.

@enocom
Copy link
Contributor Author

enocom commented Aug 24, 2018

Updated. PTAL @zombiezen, @vangent, @tbpg.

Running `wire`, we see the following:

``` shell
// wrapping the error across lines for readability
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# for a shell comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Good catch.

@enocom enocom merged commit 873f02f into google:master Aug 25, 2018
@enocom enocom deleted the wire-tutorial branch August 25, 2018 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes Google CLA has been signed!

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants