Tôi yêu Go. Và tôi cũng ghét Go (phần 1)

Tôi thích Go ngay từ đầu. Tương tự với C và Java, tôi cảm thấy rất thân thuộc với các ví dụ, các tutorials dễ hiểu để tôi có thể viết code ngay. Tôi đã từng muốn học Go vì sự nổi tiếng của ngôn ngữ này trong vài năm trở lại đây.

I Love Go

Ưu tiên đầu tiên của tôi là cố gắng để bản thân không trở nên quá khờ khạo. Tôi học được điều này từ các dev Go thực sự: họ không cần đặt ra quá nhiều câu hỏi vẫn có thể code được. Họ không theo hướng dẫn nào cả. Khi nhận ra sự nhạy cảm với độ rộng 80 cột đem đến “nỗi nhớ” không mấy thú vị, tôi đã tìm kiếm 1 line length max. Nhưng lại không có. Tuy nhiên, sau đó tôi phát hiện được gofmt – 1 công cụ đơn giản mà Go tuyển chọn để giải phóng các dev khỏi tính chuyên chế trong cú pháp của ngôn ngữ này. Nó hỗ trợ Go code và đưa nó về với One True Style. Tôi thích điều đó. Tôi sinh ra trong nền công nghiệp cơ khí với 1 hướng dẫn phong cách chuẩn xác nhưng bất kì hướng dẫn style nào cũng có lỗ hổng. Khi “phe phái” hình thái, các nhà truyền bá phong cách phải đối mặt với những người xem chỉ dẫn là tài liệu sống. Tôi đã cập nhật .vimrc từ mấy đời trước thành gofmt. Những “kẻ độc tài” Go đang trở thành chính phong cách độc tài của tôi.

Một trong những lý do khiến tôi từ bỏ C++ (98, 11, and 14) là vì nó càng ngày càng màu nhiệm. Trong khi đó, Go lại hoàn toàn đơn giản và đây chính xác là điều tôi muốn. Các thư viện và ví dụ đều dễ hiểu. Các thông báo lỗi đều không khó hiểu (nói cách khác là chúng nhanh chóng giải quyết sự nhầm lẫn về methods với các “pointer receivers”) trong khi đối lập với nó, Rust lại có những lỗi đọc như các forms thuế không thể giải thích nổi.

Lý do Go không có tính kế thừa (như các ngôn ngữ OOP khác) thực ra rất dễ để lý giải. Các interfaces của nó đều có ý nghĩa và tính thực tiễn. Bạn không cần xác định 1 cấu trúc để thực hiện 1 interface. Bạn có thể sử dụng 1 giao diện để miêu tả bất kì điều gì đang thực hiện nó. Đơn giản, đúng không? Tính năng này đặc biệt hữu ích khi bạn testing. Trong 1 môi trường hỗn loạn với các thành phần vượt qua tầm kiểm soát, bạn có thể áp dụng các interfaces vào code của người khác và sau đó đem ra thử nghiệm.

Chuỗi công cụ cũng rất dễ sử dụng – đây cũng chính là lợi ích của việc bắt đầu từ còn số không. Chuỗi công cụ sẽ hỗ trợ bạn compile, test nhanh chóng và tích hợp dễ dàng với hệ sinh thái có quy mô tốt. Tôi đã không còn lo lắng về các dependencies, rebuilding… khi biết rằng khi Go chạy, GO sẽ tìm ra các lỗi cực kì nhanh gọn ở bất kì nơi nào mà tôi đã vận hành các lỗi đó.

Và tôi cũng ghét Go

Go là 1 ngôn ngữ “ngoan cố”. Hầu hết các sản phẩm thành công đều phân định rõ ràng về những gì chúng đang có, những gì chúng không có; trong khi Go lại là 1 cá thể đặc biệt. Tôi đã bị mê hoặc bởi đặc tính rõ ràng của Go nhưng đối với 1 số dev độc đoán sẽ thấy những điều đó khá kì quái và khó chịu.

Sau khi đọc những tài liệu chính thức, tôi nhận thấy chính mình đang ở giữa giai đoạn được bắt đầu với cụm “liệu GOOS có được thiết kế cho plan9 hay không” (Plan-9 là một hệ điều hành, phiên bản mới của Unix, dự án này tới nay vẫn chưa xác định là có thật hay không). Wow. Là 1 kẻ mọt sách nghiên cứu về hệ điều hành, tôi cảm thấy có chút lạ lẫm với định nghĩ trên, nhưng tôi chưa từng thấy Plan 9. Tôi biết rằng những đồn đoán về Plan 9 là để tập hợp lại và tạo nên Golang. Thật tuyệt vời khi các khán giả nhạc pop của họ không ngăn chúng không giải quyết các B-sides cũ. Tuy kì quặc nhưng chẳng có gì sai cả.

Tôi muốn kiểm tra 1 chức năng bất biến, làm thế nào để Go thực hiện các assertions? Hey, bạn 1 lập trình viên tôi. Đó chính là cách thực hiện. Các tác giả Go luôn cương quyết với các tài nguyên bị lạm dụng thường xuyên nên họ từ chối cung cấp các tài sản đó. Vì vậy, 1 số người sử dụng một hoặc một vài thư viện có thể chạy được.

Tôi đã tạo thử 1 phép đệ quy vô hạn và bị stack overflow (tràn stack). Go chỉ cho chạy 100 frames stack đầu tiên là hết. Bạn có thể thay đổi nó nhưng tôi không thể chỉ rõ cách thức để làm được điều đó (“Go Stackoverflow” là thứ vô dụng nhất mà bạn có thể tìm thấy, bạn có thể tìm kiếm lần lượt Go và Stackoverflow). Ai đó có thể thuyết phục tôi rằng tôi chỉ muốn 100 frame đầu chứ không phải cuối cùng, không phải 4 frame tương tự hết lần này đến lần khác. Cuối cùng tôi đã phải dùng đến thiết lập runtime.debug.SetMaxStack() để giới hạn số stack. Go đã thiết lập cho nó quá lớn để bắt được các frames phù hợp và quá nhỏ để thực thi các tác vụ bình thường

Tôi đã cố gắng sử dụng các công cụ khác (ahem, DTrace) để print stack nhưng dĩ nhiên là compiler Go bỏ qua các con trỏ đến các stack mà trình debugger không thể quan sát được. Lập luận Ditto dựa trên các quy ước gọi không đủ tiêu chuẩn ABI nhưng đó là chuyện khác. Môi trường thay đổi GOEXPERIMENT=framepointer phải tổng hợp với các con trỏ khung nhưng để rebuild toàn bộ thực sự là 1 thách thức. Tất cả các con đường như đang dẫn tôi đến câu kết luận gay gắt từ 1 đồng nghiệp cũ: Golang là rác rưởi.

Dù viết code bằng Golang có thú vị đi nữa thì debug bằng Golang lại chẳng có gì vui. Có thể vì tôi không biết công cụ phù hợp. Nhưng chắc chắc sẽ không có 1 debugger với khả năng Go build để compile, Go test để testing và Go run để chạy.

Nguồn: IDE Academy via Dtrace

Chia sẻ bài viết ngay