Ngôn ngữ lập trình Go

Russ Cox


Đại học Stanford

12 tháng 1, 2010

Go

Mới

Thực nghiệm

Đồng thời

Có bộ gom rác

Ngôn ngữ

hệ thống

Hello, world

package main

import "fmt"

func main() {
	fmt.Printf("Hello, 世界\n")
}

Lịch sử

Bắt đầu thiết kế vào cuối năm 2007.

Triển khai bắt đầu hoạt động vào giữa năm 2008.

Phát hành dưới dạng dự án mã nguồn mở vào tháng 11 năm 2009.

Công việc vẫn tiếp tục.

Robert Griesemer, Ken Thompson, Rob Pike, Ian Lance Taylor, Russ Cox và nhiều người khác

Tại sao?

Nhanh hơn!

Làm cho lập trình trở nên thú vị trở lại.

Tại sao lập trình lại không còn thú vị?

Các ngôn ngữ biên dịch kiểu tĩnh (C, C++, Java) yêu cầu quá nhiều thao tác gõ phím và quá nhiều kiểu dữ liệu:

Các ngôn ngữ động (Python, JavaScript) giải quyết những vấn đề này (không cần kiểu, không cần trình biên dịch) nhưng lại đưa vào những vấn đề khác:

Liệu chúng ta có thể kết hợp điểm tốt nhất của cả hai không?

Go

Làm cho ngôn ngữ nhanh hơn.

Làm cho công cụ nhanh hơn.

Cách tiếp cận của Go: Kiểu tĩnh

Kiểu tĩnh, nhưng khai báo có thể suy luận kiểu từ biểu thức:

var one, hi = 1, "hello"

var double = func(x int) int { return x*2 }

Không phải suy luận kiểu Hindley-Milner đầy đủ.

Cách tiếp cận của Go: Phương thức

Phương thức có thể được định nghĩa trên bất kỳ kiểu nào.

type Point struct {
	X, Y float64
}

func (p Point) Abs() float64 {
	return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

Cách tiếp cận của Go: Phương thức

Phương thức có thể được định nghĩa trên bất kỳ kiểu nào.

type MyFloat float64

func (f MyFloat) Abs() float64 {
	v := float64(f)
	if v < 0 {
		v = -v
	}
	return v
}

Cách tiếp cận của Go: Kiểu trừu tượng

Một kiểu interface liệt kê một tập hợp các phương thức. Bất kỳ giá trị nào có các phương thức đó đều thỏa mãn interface.

type Abser interface {
	Abs() float64
}

func AbsPrinter(a Abser)

Có thể dùng Point hoặc MyFloat (hoặc ...):

p := Point{3, 4}
AbsPrinter(p)

f := MyFloat(-10)
AbsPrinter(f)

Lưu ý rằng Point chưa bao giờ khai báo rằng nó triển khai Abser. Nó chỉ đơn giản là làm vậy. Tương tự với MyFloat.

Cách tiếp cận của Go: Package

Một chương trình Go gồm một hoặc nhiều package.

Mỗi package là một hoặc nhiều tệp nguồn được biên dịch và nhập khẩu như một đơn vị.

package draw

type Point struct {
	X, Y int
}
package main

import "draw"

var p draw.Point

Cách tiếp cận của Go: Phạm vi hiển thị

Bên trong một package, tất cả các tên được định nghĩa cục bộ đều hiển thị trong tất cả các tệp nguồn.

Khi được nhập khẩu, chỉ các tên viết hoa mới hiển thị.

package draw

type Point struct {
	X, Y int
	dist float64
}

type cache map[Point] float64

Các client import "draw" chỉ có thể dùng các tên viết đen (in hoa).

“Shift là public mới.”

Cách tiếp cận của Go: Lập trình đồng thời

Chi phí tạo luồng điều khiển mới (goroutine) thấp:

func main() {
	go expensiveComputation(x, y, z)
	anotherExpensiveComputation(a, b, c)
}

Hai phép tính nặng chạy song song.

Cách tiếp cận của Go: Đồng bộ hóa

Dùng thông điệp tường minh để giao tiếp và đồng bộ hóa.

func computeAndSend(ch chan int, x, y, z int) {
	ch <- expensiveComputation(x, y, z)
}

func main() {
	ch := make(chan int)
	go computeAndSend(ch, x, y, z)
	v2 := anotherExpensiveComputation(a, b, c)
	v1 := <-ch
	fmt.Println(v1, v2)
}

Lưu ý việc truyền kết quả ngoài đồng bộ hóa.

Go nhanh: Ngôn ngữ

Kiểu tĩnh: đủ để biên dịch tốt, nhưng thường được suy luận.

Phương thức: trên bất kỳ kiểu nào, độc lập với hệ thống kiểu.

Kiểu trừu tượng: giá trị interface, quan hệ được suy luận tĩnh.

Phạm vi hiển thị: suy luận từ chữ hoa hay chữ thường của tên.

Lập trình đồng thời: cách nhẹ nhàng để tạo luồng điều khiển mới.

Đồng bộ hóa: truyền thông điệp tường minh, dễ dùng.


Cảm giác nhẹ nhàng như ngôn ngữ kịch bản nhưng được biên dịch.

Biên dịch nhanh

Quan sát: phần lớn thời gian biên dịch của một tệp nguồn bị dùng để xử lý các tệp khác, thường không liên quan.

Trong C: a.c include b.h, b.h include c.h, c.h include d.h.

Thực tế thường là một cây thay vì một chuỗi.

Trên Mac của tôi (OS X 10.5.8, gcc 4.0.1):

Và chúng ta vẫn chưa làm được việc gì thực sự!

Tương tự với Java, Python, nhưng đọc file nhị phân thay vì tệp nguồn.

Triển khai: Tóm tắt dependency

package gui

import "draw"

type Mouse struct {
	Loc draw.Point
	Buttons uint
}

Dạng biên dịch của gui tóm tắt phần cần thiết của draw (chỉ Point).

Triển khai: Tóm tắt dependency

Dạng biên dịch của gui tóm tắt phần cần thiết của draw (chỉ Point). Pseudo-object:

package gui
type draw.Point struct {
	X, Y int
}
type gui.Mouse struct {
	Loc draw.Point
	Buttons uint
}

Tệp import gui được biên dịch mà không cần tra cứu draw hay dependency của nó.

Trong Go: import "fmt" đọc một tệp: 184 dòng tóm tắt kiểu từ 7 package.

Ảnh hưởng nhỏ trong chương trình này nhưng có thể tăng theo hàm mũ trong các chương trình lớn.

Demo biên dịch

Build tất cả các package Go chuẩn: ~120.000 dòng mã.

Trạng thái Go

Mã nguồn mở:

  • phát hành ngày 10 tháng 11 năm 2009
  • các bản phát hành đều đặn (~ hàng tuần)
  • toàn bộ quá trình phát triển trong kho lưu trữ Mercurial công khai
  • chào đón đóng góp từ bên ngoài

Đa nền tảng:

  • FreeBSD, Linux, OS X (x86, x86-64)
  • (đang xử lý) Linux arm, Native Client x86, Windows x86.

Vẫn đang tiến triển, thực nghiệm. Những điều sắp tới:

  • bộ gom rác hoàn thiện
  • generics?
  • exception?
  • union hay kiểu tổng?

Câu hỏi?