Go for Gophers

Source: Internet
Author: User
This is a creation in Article, where the information may have evolved or changed.

Note: This article is written by Andrew Gerrand in Gophercon closing keynote
Speech on April 2014, the original address is Go for Gophers

Note: This is the video collection Watch the talk on YouTube, like the great Great Wall, need to turn over the wall inginging.

Interfaces

Interfaces: First Impressions

I used to be interested in classes and types.

Go against these:

    • No inheritance
    • No subtype polymorphism
    • No generics

Instead, it emphasizes interfaces.

The way of Interfaces:go

Go interfaces is small.

type Stringer interface {    String() string}

Stringer can print it perfectly.
Anything that implements the String is a Stringer.

A interface example

An IO. The value of Reader emits a binary data stream.

type Reader interface {    Read([]byte) (int, error)}

Like a UNIX pipeline.

Implement interfaces

// ByteReader implements an io.Reader that emits a stream of its byte value.type ByteReader bytefunc (b ByteReader) Read(buf []byte) (int, error) {    for i := range buf {        buf[i] = byte(b)    }    return len(buf), nil}

Package interfaces

type LogReader struct {    io.Reader}func (r LogReader) Read(b []byte) (int, error) {    n, err := r.Reader.Read(b)    log.Printf("read %d bytes, error: %v", n, err)    return n, err}

Use a logreader to encapsulate a single bytereader

r := LogReader{ByteReader('A')}b := make([]byte, 10)r.Read(b)fmt.Printf("b: %q", b)

By encapsulating we make up the value of the interface.

Chaining interfaces

Package wrappers to build chains:

var r io.Reader = ByteReader('A')r = io.LimitReader(r, 1e6)r = LogReader{r}io.Copy(ioutil.Discard, r)

More Concise:

io.Copy(ioutil.Discard, LogReader{io.LimitReader(ByteReader('A'), 1e6)})

Complex behavior is achieved by combining small fragments.

Programming with interfaces

Interfaces separates data from behavior.

interfaces, functions can differentiate from performance:

// Copy copies from src to dst until either EOF is reached// on src or an error occurs.  It returns the number of bytes// copied and the first error encountered while copying, if any.func Copy(dst Writer, src Reader) (written int64, err error) {
 io.Copy(ioutil.Discard, LogReader{io.LimitReader(ByteReader('A'), 1e6)})

Copy does not know the underlying data structure.

A bigger interface.

Sort. Interface describes an operation that requires ordering a collection.

type Interface interface {    Len() int    Less(i, j int) bool    Swap(i, j int)}

Intslice can sort a ints slice:

type IntSlice []intfunc (p IntSlice) Len() int           { return len(p) }func (p IntSlice) Less(i, j int) bool { return p[i] < p[j] }func (p IntSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

Sort. Sort can be sorted using Intslice a []int:

s := []int{7, 5, 3, 11, 2}sort.Sort(IntSlice(s))fmt.Println(s)

Another example of a interface

The Organ type describes a body part and it can print itself.

type Organ struct {    Name   string    Weight Grams}func (o *Organ) String() string { return fmt.Sprintf("%v (%v)", o.Name, o.Weight) }type Grams intfunc (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }func main() {    s := []*Organ{{"brain", 1340}, {"heart", 290},        {"liver", 1494}, {"pancreas", 131}, {"spleen", 162}}    for _, o := range s {        fmt.Println(o)    }}

Sort organs

Organs type how to describe and change a organs slice.

type Organs []*Organfunc (s Organs) Len() int      { return len(s) }func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

ByName and Byweight types are sorted by embedding organs with different attributes.

type ByName struct{ Organs }func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }type ByWeight struct{ Organs }func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }

By embedding we combine the types.

To sort []*organ, use byname or byweight to encapsulate it and pass it to sort. Sort:

    s := []*Organ{        {"brain", 1340},        {"heart", 290},        {"liver", 1494},        {"pancreas", 131},        {"spleen", 162},    }    sort.Sort(ByWeight{s})    printOrgans("Organs by weight", s)    sort.Sort(ByName{s})    printOrgans("Organs by name", s)

Another package

The Reverse function gets a sort. Interface and returns a sort using a inverted less method. Interface:

func Reverse(data sort.Interface) sort.Interface {    return &reverse{data}}type reverse struct{ sort.Interface }func (r reverse) Less(i, j int) bool {    return r.Interface.Less(j, i)}

To use the descending sort organs, use Reverse to combine our sort types.

    sort.Sort(Reverse(ByWeight{s}))    printOrgans("Organs by weight (descending)", s)    sort.Sort(Reverse(ByName{s}))    printOrgans("Organs by name (descending)", s)

Interfaces: Why do you do this?

They are not just very cool tricks.
This is how we structured programming in Go.

Interfaces:sigourney

Sigourney is a modular audio synthesizer that I wrote with Go.

Audio is generated by a series of Processor.

type Processor interface {    Process(buffer []Sample)}

(Github.com/nf/sigourney)

Interfaces:roshi

Roshi is a time series event store written by Peter Bourgon, which provides the API:

Insert(key, timestamp, value)Delete(key, timestamp, value)Select(key, offset, limit) []TimestampValue

The same API is implemented by the farm and cluster parts of the system:

An elegant design of the presentation combination:

(Github.com/soundcloud/roshi)

Interfaces: Why do you do this?

Interfaces is a generic programming mechanism.
They gave Go a familiar form.
Less is more.

It's all made up.
Interfaces-through design and specification-encourages us to write composable code.

The Interfaces type is only a type.
The interface value is just a value.
For other languages, they are orthogonal.

Interfaces distinguishes data from behavior. (Classes merge them).

type HandlerFunc func(ResponseWriter, *Request)func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {    f(w, r)}

Interfaces: What did I learn?

Multi-thinking combination.
Doing a lot of small things is better than doing a big and complicated thing.
And: I think the small is also quite large.
When large is beneficial, some repetition is also good.

Concurrency

Concurrency: First Impressions

My first contact with concurrency is in: C, Java, and Python.
Then: Touch the event-driven model in Python and JavaScript.

When I see Go, what I see is:

"An efficient event-driven model without callbacks."

But I still have a question: "Why can't I Wait or kill a goroutine?" ”

The way of Concurrency:go

Goroutines provides concurrent execution.

Channels indicates that communication and synchronization are using separate processes.

Select makes operations on channel operations.

A concurrency example

Comparison of the two-fork tree from the Go tour is performed.

"Implement a function

func Same(t1, t2 *tree.Tree) bool

To compare the contents of two binary trees "

Walking a tree

type Tree struct {    Left, Right *Tree    Value int}

Traversal of a simple depth-first tree:

func Walk(t *tree.Tree) {    if t.Left != nil {        Walk(t.Left)    }    fmt.Println(t.Value)    if t.Right != nil {        Walk(t.Right)    }}func main() {    Walk(tree.New(1))}

A concurrent Walker:

func Walk(root *tree.Tree) chan int {    ch := make(chan int)    go func() {        walk(root, ch)        close(ch)    }()    return ch}func walk(t *tree.Tree, ch chan int) {    if t.Left != nil {        walk(t.Left, ch)    }    ch <- t.Value    if t.Right != nil {        walk(t.Right, ch)    }}

Concurrent Walking Two trees:

func Same(t1, t2 *tree.Tree) bool {    w1, w2 := Walk(t1), Walk(t2)    for {        v1, ok1 := <-w1        v2, ok2 := <-w2        if v1 != v2 || ok1 != ok2 {            return false        }        if !ok1 {            return true        }    }}func main() {    fmt.Println(Same(tree.New(3), tree.New(3)))    fmt.Println(Same(tree.New(1), tree.New(2)))}

Do not use channels to compare trees

func Same(t1, t2 *tree.Tree) bool {    w1, w2 := Walk(t1), Walk(t2)    for {        v1, ok1 := w1.Next()        v2, ok2 := w2.Next()        if v1 != v2 || ok1 != ok2 {            return false        }        if !ok1 {            return true        }    }}

The Walk function has almost identical signatures:

func Walk(root *tree.Tree) *Walker {func (w *Walker) Next() (int, bool) {

(I can call Next instead of channel receive)

But the implementation is more complex:

func Walk(root *tree.Tree) *Walker {    return &Walker{stack: []*frame{{t: root}}}}type Walker struct {    stack []*frame}type frame struct {    t  *tree.Tree    pc int}func (w *Walker) Next() (int, bool) {    if len(w.stack) == 0 {        return 0, false    }    // continued next slide ...    f := w.stack[len(w.stack)-1]    if f.pc == 0 {        f.pc++        if l := f.t.Left; l != nil {            w.stack = append(w.stack, &frame{t: l})            return w.Next()        }    }    if f.pc == 1 {        f.pc++        return f.t.Value, true    }    if f.pc == 2 {        f.pc++        if r := f.t.Right; r != nil {            w.stack = append(w.stack, &frame{t: r})            return w.Next()        }    }    w.stack = w.stack[:len(w.stack)-1]    return w.Next()}

Another channel version

func Walk(root *tree.Tree) chan int {    ch := make(chan int)    go func() {        walk(root, ch)        close(ch)    }()    return ch}func walk(t *tree.Tree, ch chan int) {    if t.Left != nil {        walk(t.Left, ch)    }    ch <- t.Value    if t.Right != nil {        walk(t.Right, ch)    }}

But there is a problem: When inequality is found, a goroutine sent to Ch may be blocked.

Stopping early

Add a quit channel to Walker so we can stop it.

func Walk(root *tree.Tree, quit chan struct{}) chan int {    ch := make(chan int)    go func() {        walk(root, ch, quit)        close(ch)    }()    return ch}func walk(t *tree.Tree, ch chan int, quit chan struct{}) {    if t.Left != nil {        walk(t.Left, ch, quit)    }    select {    case ch <- t.Value:    case <-quit:        return    }    if t.Right != nil {        walk(t.Right, ch, quit)    }}

Create a quit channel and pass it to each walker.
When same exits, any running walkers will be interrupted by closing quit.

func Same(t1, t2 *tree.Tree) bool {    quit := make(chan struct{})    defer close(quit)    w1, w2 := Walk(t1, quit), Walk(t2, quit)    for {        v1, ok1 := <-w1        v2, ok2 := <-w2        if v1 != v2 || ok1 != ok2 {            return false        }        if !ok1 {            return true        }    }}

Why not just kill goroutines?

Goroutines is not visible in the Go code. Can't kill it or wait.

You've built it yourself.

Here is the reason:
Once the Go code knows which thread it is running, you can get thread-locality.
Thread-locality causes the concurrency model to fail.

Concurrency:why It works

This model makes the concurrent code readable and writable.
(which makes concurrency understandable.)

Encourage decomposition of independent calculations.

The simple concurrency model makes it flexible enough.
Channels are only values that are suitable for the correct type system.

Goroutines is not visible in the Go code, which allows you to concurrency from anywhere.

Less is more.

Concurrency: What did I learn?

Concurrency is not just doing more things faster.
Write better code.

Grammar

Syntax: First Impressions

First, the grammar of Go is not rigid and lengthy.
I'm used to the convenience it offers.

For example:

    • There is no getters/setters in the attribute
    • No map/filter/reduce/zip.
    • No optional parameters

The way of Syntax:go

Readability is better than anything else.
Providing enough syntactic sugar makes it efficient, but not too much.

Getters and Setters (or "properties")

Getters and setters make assignments and reads into function calls.
This can lead to surprising hidden behavior.

In Go, only the write (and call) method.

Control flow is not obscured.

Map/filter/reduce/zip

Map/filter/reduce/zip is very useful in Python:

a = [1, 2, 3, 4]b = map(lambda x: x+1, a)

In Go, you can only write loops.

a := []int{1, 2, 3, 4}b := make([]int, len(a))for i, x := range a {    b[i] = x+1}

It's a bit verbose.
But it makes the performance characteristic more obvious.

It's easy to write code, and you get more control.

Optional parameters

The Go function has no optional parameters.

Use function changes instead:

func NewWriter(w io.Writer) *Writerfunc NewWriterLevel(w io.Writer, level int) (*Writer, error)

Or use an options struct:

func New(o *Options) (*Jar, error)type Options struct {    PublicSuffixList PublicSuffixList}

Or a variable list of options.

Create small and simple things, not big and complex things.

Syntax:why It works

The language rejects complex code.

With the obvious control flow, it is very easy to get into unfamiliar code.

Instead, we create more things that make it very easy to record documents and understand them.

So the Go code is very easy to read.

(using GOFMT will make the code more readable.)

Syntax: What did I learn?

I am very smart for myself.

I appreciate the consistency, clarity and transparency of the Go code.

I sometimes lose convenience, but rarely.

Error handling

Error handling: First Impressions

I've used exceptions to handle errors before.

By comparison, the error handling model of Go is very lengthy.

I was immediately disgusted to type this:

if err != nil {    return err}

The way to Error Handling:go

The Go uses built-in error interface coding errors:

type error interface {    Error() string}

The value of Error is used just like any other value.

func doSomething() errorerr := doSomething()if err != nil {    log.Println("An error occurred:", err)}

The code for error handling is just code.

(with a convention (OS). Error), in Go 1 is built-in.

Error handling:why It works

Error handling is introduced.

Go makes error handling as important as any other code.

Errors are just values, and they are easily incorporated into other parts of the language (interfaces, channels, etc.).

The result: the Go code processing error is correct and elegant.

We use the same language for errors.
There is no hidden control flow (throw/try/catch/finally) that improves readability.

Less is more.

Error handling: What have I learned

In order to write better code, you must consider error handling.

Exceptions makes it very easy to avoid thinking errors.
(the error should not be an exception)

Go encourages us to consider every kind of error situation.

My Go program is more robust than my other programs.
(I won't miss the mistake at all.) )

Packages

Packages: First Impressions

I found the rules of capital-letter-visibility very strange;
"Let me use my own naming scheme!" ”

I don't like one package per directory;
"Let me use my own structure!" ”

I am very disappointed with the lack of monkey patching.

The way of Packages:go

Go packages is a namespace for types, functions, variables, and constants.

Visibility

Visibility at the package level.

When they use a capital letter, the Names is exported.

package zipfunc NewReader(r io.ReaderAt, size int64) (*Reader, error) // exportedtype Reader struct {    // exported    File    []*File     // exported    Comment string      // exported    r       io.ReaderAt // unexported}func (f *File) Open() (rc io.ReadCloser, err error)   // exportedfunc (f *File) findBodyOffset() (int64, error)        // unexportedfunc readDirectoryHeader(f *File, r io.Reader) error  // unexported

Good readability: It's easy to know if a name is part of a public interface
Good design: Couples naming decisions with interface decisions

Package structure

Packages can propagate across multiple files.

Allows sharing of private implementations and informal code organization.

The Packages file must exist in the unique directory of the package.

The path to the directory is absolutely the path of the import.

Build system lookup dependencies are independent from the source code.

"Monkey Patching"

GO prohibits modifying a package's declaration from outside the package.

But we can use global variables to implement similar behavior:

package flagvar Usage = func() {    fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])    PrintDefaults()}

or register the function:

package httpfunc Handle(pattern string, handler Handler)

This gives monkey patching enough flexibility but pay attention to the terms of the author on the package.

(This depends on the initialization semantics of Go).

Packages:why They work

Loose package organization makes it easy for us to write code and refactor code.

But the package encourages programmers to consider public interfaces.

This leads to a good naming and simple interface.

Sources as the only trustworthy source, they have no makefiles to synchronize.

(This involves prompting good tools such as godoc.org and Goimports).

The predictable semantics make the package very easy to read, understand and use.

Packages: What did I learn?

The Go package teaches me to prioritize the users of my code.
(even if the user is me)

It also stopped me from doing disgusting things.

In any case, the package is accurate.
That's a good feeling.

Maybe it's part of my favorite language.

Documentation

Documentation: First Impressions

Godoc reads documents from the Go source, like Pydoc or Javadoc.

However, unlike these two, it does not support complex formats or other meta-data.

Why?

The way of Documentation:go

Godoc Note Before an exported declaration identifier:

// Join concatenates the elements of a to create a single string.// The separator string sep is placed between elements in the resulting string.func Join(a []string, sep string) string {

It extracts the annotations and displays them:

$ godoc strings Joinfunc Join(a []string, sep string) string    Join concatenates the elements of a to create a single string. The    separator string sep is placed between elements in the resulting string.

Also integrates the test framework to provide examples of test functions:

func ExampleJoin() {    s := []string{"foo", "bar", "baz"}    fmt.Println(strings.Join(s, ", "))    // Output: foo, bar, baz}

Documentation:why It works

Godoc want you to write better comments, so the source looks good:

// ValidMove reports whether the specified move is valid.func ValidMove(from, to Position) bool

Javadoc only wants to produce beautiful documents, so the source code looks ugly.

/** * Validates a chess move. * * @param fromPos  position from which a piece is being moved * @param toPos    position to which a piece is being moved * @return         true if the move is valid, otherwise false */boolean isValidMove(Position fromPos, Position toPos)

(a "Validmove" grep returns the first line of the document)

Documentation: What did I learn?

Godoc taught me to write a document just like writing code.
Writing a document improved my ability to write code.

More

There are many examples here.

The most important topics:

    • First, some things look strange or lacking.
    • I realized that it was a design decision.

These decisions make this language-and Go code-better

Sometimes you should live with a language for a while to see it.

Lessons learned

The code is used for communication.

Speak clearly:

    • Choose a good name
    • Design a simple interface
    • Write a precise document
    • Don't be smart.

Less that is more

The new feature will weaken the existing feature.
Features increase complexity.
Complexity defeats Orthogonality. ----complexity defeats Orthogonality (is this really translated?) Seeking the great God).
Orthogonality is crucial-it facilitates the combination.

Combination is the key

Don't try to solve the problem by building a thing.
Combine simple tools and make them instead.

Well-designed interface

    • Don't overdo it.
    • Find the key point (Bull's Eye)
    • Don't be too rough.

Simplification is difficult.

Take the time to find a simple solution.

The impact of Go on me

These lessons are everything I know.

Go helps me to recognize them.

Go makes me become a better programmer.

A message to the Gophers of any place

Let's build small, simple, beautiful things together.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.