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:
- dài dòng, lặp lại nhiều
- quá chú trọng vào phân cấp kiểu
- kiểu dữ liệu cản trở nhiều như giúp ích
- biên dịch mất quá nhiều thời gian
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:
- lỗi xảy ra lúc chạy thay vì bị phát hiện tĩnh
- không có biên dịch nghĩa là mã chạy chậm
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):
- C:
#include <stdio.h> đọc 360 dòng từ 9 tệp.
- C++:
#include <iostream> đọc 25.326 dòng từ 131 tệp.
- Objective C:
#include <Carbon/Carbon.h> đọc 124.730 dòng từ 689 tệp.
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?