paint-brush
Đặc điểm Mojo: Chúng so sánh với giao diện Go như thế nào?từ tác giả@a2svior
1,666 lượt đọc
1,666 lượt đọc

Đặc điểm Mojo: Chúng so sánh với giao diện Go như thế nào?

từ tác giả Valentin Erokhin6m2023/12/26
Read on Terminal Reader

dài quá đọc không nổi

Hướng dẫn cách làm việc với Đặc điểm Mojo kèm theo các ví dụ và so sánh Đặc điểm Mojo với Giao diện cờ vây. Những hạn chế và cảnh báo của đặc điểm Mojo.
featured image - Đặc điểm Mojo: Chúng so sánh với giao diện Go như thế nào?
Valentin Erokhin HackerNoon profile picture
0-item

Gần đây, các đặc tính đã được giới thiệu trong Mojo nên tôi nghĩ mình sẽ thử chúng. Hiện tại, các đặc điểm tích hợp bao gồm CollectionElement , Copyable , Destructable , Intable , Movable , SizedStringable (có vẻ như hậu tố "-able" là một thứ trong các quy ước đặt tên này!).

Đặc điểm hoạt động trên cấu trúc. Để triển khai một đặc điểm, bạn chỉ cần thêm một phương thức vào cấu trúc phù hợp với đặc điểm đó; sau đó chuyển tên đặc điểm làm tham số:

 @value struct Duck(Quackable): fn quack(self): print("Quack!")


Trình trang trí @value trong Mojo chèn các phương thức vòng đời như __init__() , __copyinit__()__moveinit__() vào cấu trúc, đơn giản hóa cuộc sống của chúng ta một chút vì chúng ta không phải tự thêm chúng.


Các đặc điểm trong Mojo chưa hỗ trợ triển khai mặc định cho các phương thức, do đó, ... nằm trong phần nội dung của phương thức Quackable ở trên. Bạn cũng có thể sử dụng pass , điều này sẽ có tác dụng tương tự như trong Python.

Đặc điểm Mojo so với giao diện cờ vây

Mặc dù có tên khác nhưng cách tiếp cận cơ bản về các đặc điểm trong Mojo khiến tôi nhớ đến giao diện Go. Trong Go, bạn sẽ xác định cấu trúc Duck và triển khai giao diện Quackable như thế này:


 type Quackable interface { quack() }


Và để tạo một cấu trúc thỏa mãn giao diện này:


 type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") }


So sánh nó với việc triển khai Mojo:


 trait Quackable: fn quack(self): ... @value struct Duck(Quackable): fn quack(self): print("Quack!")


Tôi nghĩ phiên bản Mojo thậm chí còn ngắn gọn hơn, lồng định nghĩa phương thức quack() bên trong kiểu cấu trúc Duck . Ngoài việc làm việc trên các cấu trúc, các đặc điểm trong Mojo không yêu cầu từ khóa implements , giống như trong Go.


Điều thú vị là, tài liệu Mojo đã nêu rõ rằng việc triển khai mặc định chưa được phép, nghĩa là chúng có thể được cho phép trong tương lai. Điều này có nghĩa là Mojo có thể theo đuổi một cách tiếp cận khác với Go, tập trung vào các cấu trúc thỏa mãn giao diện chứ không phải triển khai nó.


Việc triển khai phương thức mặc định là không cần thiết trong Go và đạt được hiệu quả tương tự khi nhúng, vì việc triển khai giao diện/đặc điểm đơn giản là một khái niệm không tồn tại trong Go. Mọi thứ có thể khác ở Mojo.

Cách sử dụng đặc điểm Mojo

Giá trị của các đặc điểm và giao diện là làm cho mã có thể tái sử dụng được. Ví dụ: trong Mojo, bạn có thể viết các hàm chấp nhận các loại đặc điểm…


 fn make_it_quack[T: Quackable](could_be_duck: T): could_be_duck.quack()


Và sau đó chuyển các cấu trúc khác nhau triển khai đặc điểm giống như đầu vào - mọi thứ sẽ hoạt động bình thường! Ví dụ: đây là cấu trúc Goose triển khai Quackable :


 @value struct Goose(Quackable): fn quack(self): print("Honk!")


Và ở đây, để gọi make_it_quack trên cả GooseDuck (hãy nhớ rằng, bạn cần hàm main trong Mojo làm điểm vào chương trình của mình):


 def main(): make_it_quack(Duck()) make_it_quack(Goose())


Đầu ra của điều này sẽ là


 Quack! Honk!


Lỗi đặc điểm

Nếu tôi cố gắng chuyển thứ gì đó không triển khai đặc điểm Quackable vào hàm make_it_quack , giả sử StealthCow , chương trình sẽ không biên dịch:


 @value struct StealthCow(): pass


 make_it_quack(StealthCow())


Thông báo lỗi bên dưới có thể mang tính mô tả nhiều hơn; có lẽ nhóm Mojo sẽ cải thiện nó?


 error: invalid call to 'make_it_quack': callee expects 1 input parameter, but 0 were specified


Tương tự nếu tôi loại bỏ phương thức quack khỏi Goose ; ở đây, chúng tôi nhận được một lỗi mô tả hay:


 struct 'Goose' does not implement all requirements for 'Quackable' required function 'quack' is not implemented


Ưu điểm của loại kiểm tra kiểu tĩnh này so với phương pháp Python thuần túy là chúng ta có thể dễ dàng phát hiện lỗi và tránh chuyển bất kỳ lỗi nào sang quá trình sản xuất vì mã sẽ không được biên dịch.

Di sản

Các đặc điểm trong Mojo đã hỗ trợ tính kế thừa, vì vậy đặc điểm `Quackable` của chúng tôi có thể mở rộng đặc điểm `Audible` như sau:


 trait Audible: fn make_sound(self): ...


 trait Quackable(Audible): fn quack(self): ...


Điều này có nghĩa là cấu trúc Duck sẽ phải triển khai cả quackmake_sound để phù hợp với đặc điểm Quackable .


Điều này tương tự như khái niệm "Nhúng giao diện" trong Go, để tạo giao diện mới kế thừa từ các giao diện khác, bạn sẽ nhúng giao diện chính như thế này:


 type Quackable interface { Audible // includes methods of Audible in Quackable's method set }

Phương thức tĩnh

Các đặc điểm cũng chấp nhận các phương thức tĩnh hoạt động mà không cần tạo phiên bản của cấu trúc:


 trait Swims: @staticmethod fn swim(): ... @value struct Duck(Quackable, Swims): fn quack(self): print("Quack!") @staticmethod fn swim(): print("Swimming")


 fn make_it_swim[T: Swims](): T.swim()


Bạn gọi một phương thức tĩnh như thế này:


 def main(): make_it_quack(Duck()) make_it_quack(Goose()) Duck.swim()


Cái nào sẽ xuất ra:


 Quack! Honk! Swimming


Lưu ý rằng trong lệnh gọi phương thức cuối cùng, phiên bản Duck không được tạo. Đây là cách các phương thức tĩnh trong Python hoạt động, khác xa với lập trình hướng đối tượng. Mojo được xây dựng dựa trên chức năng Python này.

Hạn chế của đặc điểm Mojo so với giao diện cờ vây

Điều thú vị là thủ thuật cờ vây có interface{} cho phép vượt qua bất kỳ loại nào và phổ biến với cộng đồng cờ vây trước khi Go Generics được giới thiệu, sẽ không hoạt động với hàm được gõ Mojo fn .


Cấu trúc của bạn phải triển khai ít nhất một trong các phương thức vòng đời như __len__ hoặc __str__ , trong trường hợp này, sẽ làm cho nó tuân theo Sized hoặc Stringable , để được sử dụng với các hàm chấp nhận các loại đặc điểm.


Đây thực sự không phải là một hạn chế thực sự và rất có ý nghĩa trong Mojo, vì với Mojo, nếu bạn cần gõ động, bạn có thể quay lại hàm def linh hoạt hơn, sau đó áp dụng phép thuật Python thông thường để làm việc với ẩn số. các loại.


Các hàm Mojo fn nghiêm ngặt hơn cũng hoạt động trên các cấu trúc chung sử dụng các kiểu như DynamicVector ; đọc thêm về phần đó ở đay .


Một hạn chế khác mà tôi thấy liên quan đến các cấu trúc và phương thức của chúng hơn là các đặc điểm và là một trở ngại tiềm ẩn trong việc triển khai Kiến trúc sạch/tách mã thành các phần khác nhau.


Hãy xem xét một trong những ví dụ trước đây với định nghĩa phương thức cấu trúc Go và Mojo:


 type Duck struct {} func (d Duck) quack() { fmt.Println("Quack!") }


 @value struct Duck(Quackable): fn quack(self): print("Quack!")


Ví dụ Mojo, theo cú pháp giống Python hơn, lồng định nghĩa phương thức trực tiếp bên trong cấu trúc, trong khi phiên bản Go cho phép nó tách nó khỏi chính cấu trúc đó. Trong phiên bản này, nếu tôi có một cấu trúc rất dài với nhiều loại và phương thức thì sẽ khó đọc hơn một chút.


Tuy nhiên, đó không phải là sự khác biệt quan trọng mà chỉ là điều cần lưu ý.


Các đặc điểm Mojo đã khá hữu ích, mặc dù ngôn ngữ này mới ở giai đoạn đầu. Mặc dù triết lý của Go là sự đơn giản và cố gắng giữ các tính năng ở mức tối thiểu, nhưng có khả năng chúng ta sẽ thấy nhiều chức năng hơn được thêm vào các đặc điểm của Mojo trong tương lai, khiến chúng thậm chí còn mạnh mẽ hơn và cho phép thực hiện nhiều trường hợp sử dụng khác nhau.