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 Object
và 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ó bark
hoạ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 bark
hà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 Barkable
ràng buộc:
makeItBark :: Barkable a => a -> unit
makeItBark barker = bark (barker)
Điều này nói rằng makeItBark
sẽ làm việc cho bất kỳ loại đáp ứng Barkable
yêu cầu. Điều này có vẻ tương tự như interface
trong 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 đó Duck
là Barkable
bất cứ lúc nào, ngay cả khi đó Duck
là 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 Duck
không viết một bark
hàm - tôi có thể cung cấp nó sau khi thực tế khi tôi nói ngôn ngữ Duck
thỏa mãn Barkable
:
instance Barkable Duck where
bark d = quack (punch (d))
makeItBark (aDuck)
Điều này nói rằng Duck
s 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 makeItBark
vịt.
Standard ML
và OCaml
thậ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 > 5
vì 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 bark
trê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 if
tuyê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.