Các ngôn ngữ được gõ động là uni-gõ
So sánh các hệ thống loại , không có lợi thế trong việc gõ động. Gõ động là một trường hợp đặc biệt của gõ tĩnh - đó là ngôn ngữ gõ tĩnh trong đó mọi biến có cùng loại. Bạn có thể đạt được điều tương tự trong Java (trừ đi sự đồng nhất) bằng cách làm cho mọi biến đều có kiểu Objectvà có các giá trị "đối tượng" là kiểu Map<String, Object>:
void makeItBark(Object dog) {
Map<String, Object> dogMap = (Map<String, Object>) dog;
Runnable bark = (Runnable) dogMap.get("bark");
bark.run();
}
Vì vậy, ngay cả khi không có sự phản chiếu, bạn có thể đạt được hiệu quả tương tự chỉ trong bất kỳ ngôn ngữ gõ tĩnh nào, thuận tiện cú pháp sang một bên. Bạn không nhận được bất kỳ sức mạnh biểu cảm bổ sung; ngược lại, bạn có sức mạnh biểu cảm ít hơn bởi vì trong một ngôn ngữ được gõ động, bạn bị từ chối khả năng hạn chế các biến đối với một số loại nhất định.
Làm cho một con vịt sủa trong một ngôn ngữ gõ tĩnh
Hơn nữa, một ngôn ngữ gõ tĩnh tốt sẽ cho phép bạn viết mã hoạt động với bất kỳ loại nào có barkhoạt động. Trong Haskell, đây là một loại lớp:
class Barkable a where
bark :: a -> unit
Điều này thể hiện ràng buộc rằng đối với một số loại ađược coi là Barkable, phải tồn tại một barkhàm lấy giá trị của loại đó và không trả về gì.
Sau đó, bạn có thể viết các hàm chung theo các Barkableràng buộc:
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
Điều này nói rằng makeItBarksẽ làm việc cho bất kỳ loại đáp ứng Barkableyêu cầu. Điều này có vẻ tương tự như interfacetrong Java hoặc C # nhưng nó có một lợi thế lớn - các loại không phải xác định trước loại lớp nào chúng đáp ứng. Tôi có thể nói rằng loại đó Ducklà Barkablebất cứ lúc nào, ngay cả khi đó Ducklà loại bên thứ ba tôi không viết. Trên thực tế, không có vấn đề gì khi người viết Duckkhông viết một barkhàm - tôi có thể cung cấp nó sau khi thực tế khi tôi nói ngôn ngữ Duckthỏa mãn Barkable:
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
Điều này nói rằng Ducks có thể sủa, và chức năng vỏ cây của chúng được thực hiện bằng cách đấm vịt trước khi làm cho nó quẫy. Với cách đó, chúng ta có thể gọi makeItBarkvịt.
Standard MLvà OCamlthậm chí linh hoạt hơn ở chỗ bạn có thể đáp ứng cùng loại lớp theo nhiều cách. Trong các ngôn ngữ này, tôi có thể nói rằng các số nguyên có thể được đặt hàng bằng cách sử dụng thứ tự thông thường và sau đó quay lại và nói rằng chúng cũng có thể được sắp xếp theo cách chia (ví dụ: 10 > 5vì 10 chia hết cho 5). Trong Haskell, bạn chỉ có thể khởi tạo một lớp loại một lần. (Điều này cho phép Haskell để tự động biết rằng nó ok để gọi barktrên một con vịt; trong SML hoặc OCaml bạn phải rõ ràng về mà bark chức năng bạn muốn, bởi vì có thể có nhiều hơn một.)
Sự đồng tình
Tất nhiên, có sự khác biệt về cú pháp. Mã Python mà bạn đã trình bày ngắn gọn hơn nhiều so với mã Java tương đương mà tôi đã viết. Trong thực tế, sự đồng nhất đó là một phần lớn của sức hấp dẫn của các ngôn ngữ được gõ động. Nhưng suy luận kiểu cho phép bạn viết mã giống như ngôn ngữ được gõ tĩnh, bằng cách giải phóng bạn khỏi việc phải viết rõ ràng các loại của mỗi biến. Một ngôn ngữ được gõ tĩnh cũng có thể cung cấp hỗ trợ riêng cho việc gõ động, loại bỏ tính dài dòng của tất cả các thao tác đúc và ánh xạ (ví dụ: C # 's dynamic).
Các chương trình đúng nhưng không đúng
Để công bằng, gõ tĩnh nhất thiết phải loại trừ một số chương trình đúng về mặt kỹ thuật mặc dù trình kiểm tra loại không thể xác minh được. Ví dụ:
if this_variable_is_always_true:
return "some string"
else:
return 6
Hầu hết các ngôn ngữ gõ tĩnh sẽ từ chối iftuyên bố này , mặc dù nhánh khác sẽ không bao giờ xảy ra. Trong thực tế, dường như không ai sử dụng loại mã này - bất cứ điều gì quá thông minh đối với trình kiểm tra loại có thể sẽ khiến những người duy trì mã trong tương lai của bạn nguyền rủa bạn và người thân của bạn. Trường hợp cụ thể, ai đó đã dịch thành công 4 dự án Python nguồn mở sang Haskell , điều đó có nghĩa là họ không làm bất cứ điều gì mà một ngôn ngữ gõ tĩnh tốt không thể biên dịch. Hơn nữa, trình biên dịch đã tìm thấy một vài lỗi liên quan đến loại mà đơn vị kiểm tra không bắt được.
Đối số mạnh nhất tôi từng thấy khi gõ động là macro của Lisp, vì chúng cho phép bạn tùy ý mở rộng cú pháp của ngôn ngữ. Tuy nhiên, Typed Rquet là một phương ngữ được gõ tĩnh của Lisp có macro, vì vậy có vẻ như gõ tĩnh và macro không loại trừ lẫn nhau, mặc dù có lẽ khó thực hiện đồng thời hơn.
Táo và cam
Cuối cùng, đừng quên rằng có sự khác biệt lớn hơn về ngôn ngữ so với chỉ hệ thống loại của chúng. Trước Java 8, thực hiện bất kỳ loại lập trình chức năng nào trong Java thực tế là không thể; một lambda đơn giản sẽ yêu cầu 4 dòng mã lớp ẩn danh. Java cũng không hỗ trợ cho các bộ sưu tập bằng chữ (ví dụ [1, 2, 3]). Cũng có thể có sự khác biệt về chất lượng và tính sẵn có của công cụ (IDE, trình gỡ lỗi), thư viện và hỗ trợ cộng đồng. Khi ai đó tuyên bố là có năng suất cao hơn trong Python hoặc Ruby so với Java, sự khác biệt về tính năng đó cần phải được tính đến. Có một sự khác biệt giữa so sánh các ngôn ngữ với tất cả các loại pin , lõi ngôn ngữ và hệ thống loại .
makeItBark(collections.namedtuple("Dog", "bark")(lambda x: "woof woof")). Đối số đó thậm chí không phải là một lớp , nó là một ẩn danh có tên là tuple. Gõ vịt ("nếu nó giống như ...") cho phép bạn thực hiện các giao diện đặc biệt với các hạn chế về cơ bản bằng không và không có cú pháp cú pháp. Bạn có thể làm điều này bằng một ngôn ngữ như Java, nhưng cuối cùng bạn có rất nhiều sự phản ánh lộn xộn. Nếu một hàm trong Java yêu cầu ArrayList và bạn muốn cung cấp cho nó một kiểu bộ sưu tập khác, thì bạn là SOL. Trong con trăn thậm chí không thể đi lên.