Goのどのあたりがよいか

GoConference 2013 Spring

鵜飼文敏

Fumitoshi Ukai / Go contributor

第一印象

あるエンジニアの感想

When I first started programming in Go, I didn't like it very much at all.

...

I really do like Go now.

...

I also find it difficult to explain why I enjoy programming in Go.

最初気にいらない理由

C++/Java/Pythonなどと似ているようで、いろいろ違う。

Goの設計方針

Many of the decisions in Go are motivated by experience working on and maintaining large code bases at Google.

Goのよいところ

型宣言

C++やJavaなど

int i;

Go

var i int

なんで逆なの?

複雑な型宣言

C++

int (*f(int x))(int (*g)(int x), int y);

Java

interface Func1 {
  int invoke(int x);
};
interface Func2 {
  Func1 invoke(Func1 g, int y);
};
Func2 f;  // {{int => int}, int => {int => int}} f;

Go

var f func (g func(x int) int, y int) func (x int) int

型は必要?

class Duck:
  def quack(self):
    print("Quaaaack!")
  def feathers(self):
    print("The duck has white and gray feathers.")

def in_the_forest(duck):
   duck.quack()
   duck.feathers()
def game():
   donald = Duck()
   in_the_forest(donald)
# cf. http://en.wikipedia.org/wiki/Duck_typing

ダックタイピング: When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck

コードが複雑になってくるとだんだんわからなくなる...!

interface 型

package main
import "fmt"

type Duck string
func (duck Duck) quack() { fmt.Println("Quaack!") }
func (duck Duck) feathers() {
     fmt.Printf("%s has white and gray feathers.\n", duck)
}

type Ducker interface {
     quack()
     feathers()
}

func inTheForest(duck Ducker) {
     duck.quack()
     duck.feathers()
}
func main() {
     donald := Duck("donald")
     inTheForest(donald)
}

オブジェクト指向: クラスは?

C++

class Foo { public: void Method(); ... };

void Foo::Method() { ... }

Java

class Foo { public void Method() { ... } ... };

Python

class Foo:
  def Method(self):
    ...
  ...

Goのオブジェクト指向

package main
import "fmt"

type Foo struct {
     Name string
     x, y int
}
func (f Foo) Print(s string) {
     fmt.Printf(s, f.Name, f.x, f.y)
}
func fooPrint(f Foo, s string) {
     fmt.Printf(s, f.Name, f.x, f.y)
}
func main() {
     f := Foo{Name:"foo", x:1, y:2}
     f.Print("name:%s, x:%d, y:%d\n")
     fooPrint(f, "name:%s, x:%d, y:%d\n")
     print := Foo.Print
     print(f, "name:%s, x:%d, y:%d\n")
}

オブジェクト指向: 継承は?

C++

class Foo : public Bar, Baz { ... }

Java

class Foo extends Bar implements Baz { ... }

Python

class Foo(Bar, Baz):
  ...

型の埋め込み

package main

import "fmt"

type Bar struct { x int }
func (bar Bar) GetX() int { return bar.x }
func (bar Bar) Get() int { return bar.x }
type Baz struct { y int }
func (baz Baz) GetY() int { return baz.y }

type Foo struct { Bar; Baz }
func (f Foo) Get() int { return f.Bar.x + f.Baz.y }

func main() {
     bar := Bar{x: 1}
     baz := Baz{y: 2}
     foo := Foo{Bar: bar, Baz: baz}
     fmt.Printf("%#v\n", foo)
     fmt.Printf("X = %d\n", foo.GetX())
     fmt.Printf("Y = %d\n", foo.GetY())
     fmt.Printf("%d\n", foo.Get())
}

型の埋め込みじゃない場合

package main

import "fmt"

type Bar struct { x int }
func (bar Bar) GetX() int { return bar.x }
func (bar Bar) Get() int { return bar.x }
type Baz struct { y int }
func (baz Baz) GetY() int { return baz.y }

type Foo struct { bar Bar; baz Baz }
func (f Foo) Get() int { return f.bar.x + f.baz.y }

func main() {
     bar := Bar{x: 1}
     baz := Baz{y: 2}
     foo := Foo{bar: bar, baz: baz}
     fmt.Printf("%#v\n", foo)
     fmt.Printf("X = %d\n", foo.GetX())  // error!
     fmt.Printf("Y = %d\n", foo.GetY())  // error!
     fmt.Printf("%d\n", foo.Get())
}

スコープ

C++/Java

public
protected
private

Go

putting the visibility in the name rather than its type means that it's always clear when looking at an identifier whether it is part of the public API. After using Go for a while, it feels burdensome when going back to other languages that require looking up the declaration to discover this information.

未使用なコード

package main
import "fmt"
func main() {
     x := 0  // error!
     fmt.Println("Hello, world")
}
package main
import "fmt"  // error!
func main() {
     x := 0
     // fmt.Println("Hello, world")
}

使われなくなったコードを削除するのは大変。

未使用なコードを残したい

package main

import "fmt"

var _ = fmt.Println

func main() {
     x := 0
     _ = x
     // fmt.Println("Hello, world")
}

エラー処理

Java

byte[] value;
try {
    value = Get(x);
} catch (<exception to catch>) {
    /* handle exception */
}
/* use value */

Python

try:
  value = Get(x)
except <exception to catch>
  # handle exception
# use value

エラー処理

Go

if value, err := Get(x); err != nil {
   /* handle error */
} else {
   /* use value */
}

At first, I found it annoying, but the ethic of handling error at the point whey occurred made me think more carefully about error handling as I wrote the code, and the code ended up better for it.

最近の印象

Thank you

鵜飼文敏

Fumitoshi Ukai / Go contributor

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)