Có thể có một ngôn ngữ gõ động mà không cần gõ vịt? [đóng cửa]


9

Câu hỏi này đã được hỏi ở đây , nhưng nhận được câu trả lời kém và không làm rõ vấn đề. Tôi tin rằng nó biện minh cho việc hỏi lại.

Tôi hiểu rằng bạn có thể gõ vịt bằng các ngôn ngữ được nhập động hoặc với các ngôn ngữ được nhập tĩnh (nhưng ví dụ về những ngôn ngữ này rất hiếm, chẳng hạn như các mẫu của C ++).

Tuy nhiên tôi không chắc có ngôn ngữ nào được gõ động mà không cần gõ vịt hay không .

Gõ vịt có nghĩa là loại đối tượng dựa trên các hoạt động và thuộc tính mà nó có tại một thời điểm nhất định. Có cách nào để gõ động mà không cần hỗ trợ gõ vịt không?

Chúng ta hãy xem mã Python này để tìm hiểu:

def func(some_object)
    some_object.doSomething()

something = Toaster()
func(something)

Trong các ngôn ngữ được gõ động, loại đối tượng chỉ được biết khi chạy. Vì vậy, khi bạn cố gắng thực hiện một thao tác trên nó (ví dụ some_object.doSomething()), thời gian chạy chỉ có một lựa chọn - đó là kiểm tra xem loại some_objecthỗ trợ doSomething()đó, chính xác là gõ vịt là gì.

Vì vậy, có thể có một ngôn ngữ gõ động mà không cần gõ vịt? Vui lòng giải thích.


1
Tôi đã kiểm tra một vài ngôn ngữ (Javascript, Ruby và PHP) trên Wikipedia và ngôn ngữ thứ hai được cho là năng động và yếu, không được gõ. Điều đó có thể trả lời câu hỏi của bạn.
Trylks

Trên thực tế, bộ thực thi có một lựa chọn để kiểm tra chữ ký loại của đối tượng trước khi thử truy cập vào bất kỳ trường nào của nó hoặc gọi các phương thức của nó. Đó sẽ là "gõ động mạnh mẽ". Lưu ý, ví dụ, Python đang hoạt động một phần theo cách này - giả sử, nó rõ ràng gây ra lỗi loại khi bạn thử 1 + "1". Trong trường hợp của Python, quy tắc kiểm tra gần như không có và việc triển khai mã người dùng để kiểm tra các loại nếu người dùng (trái ngược với thời gian chạy của Python) thấy nó hữu ích. Cũng lưu ý rằng gõ vịt so với gõ không phải vịt là giống nhau so với gõ cấu trúc (xem Wikipedia).
Michael Pankov

có lẽ bạn nên sử dụng một con ngỗng thay vì một con vịt để đánh máy cho bạn?
jwenting

Câu trả lời:


19

Đầu tiên, để chắc chắn rằng chúng ta đang nói về những điều tương tự, tôi sẽ bắt đầu với một số định nghĩa.

Gõ tĩnh có nghĩa là lỗi loại được báo cáo tại thời điểm biên dịch, trong khi gõ động có nghĩa là lỗi loại được báo cáo khi chạy.

Gõ vịt có nghĩa là một đoạn mã yêu cầu một đối tượng hỗ trợ các hoạt động được sử dụng và không có gì nữa.

Gõ cấu trúc yêu cầu một đối tượng hỗ trợ một tập hợp các thao tác nhất định (ngay cả khi một số trong số chúng có thể không được sử dụng).

Gõ danh nghĩa yêu cầu đối tượng đó chính xác là kiểu đã cho hoặc là kiểu con của kiểu đó.

Vì vậy, như bạn có thể thấy, gõ cấu trúc chặt chẽ hơn gõ vịt và gõ danh nghĩa chặt chẽ hơn cấu trúc.

Bây giờ, tôi sẽ nói về ngôn ngữ TypeScript , vì nó minh họa độc đáo hầu hết các tùy chọn này.

Hãy xem xét chương trình TypeScript sau đây:

interface Person {
    Name : string;
    Age : number;
}

function greet(person : Person) {
    alert("Hello, " + person.Name);
}

greet({ Name: "svick" });

Do đối tượng được truyền vào greetkhông có thuộc Agetính, điều này gây ra lỗi thời gian biên dịch, chứng tỏ TypeScript sử dụng kiểu gõ cấu trúc tĩnh .

Mặc dù có lỗi, mã ở trên thực sự biên dịch thành JavaScript sau, chạy tốt:

function greet(person) {
    alert("Hello, " + person.Name);
}

greet({ Name: "svick" });

Điều này cho thấy TypeScript cũng sử dụng kiểu gõ vịt động .

Nếu mã được biên dịch thành một cái gì đó như:

function greet(person) {
    if (!(typeof(person.Name) == 'string' && typeof(person.Age) == 'number'))
        throw 'TypeError';

    alert("Hello, " + person.Name);
}

Sau đó, đó sẽ là một ví dụ về kiểu gõ cấu trúc động , bởi vì nó kiểm tra xem đối tượng có các thuộc tính bắt buộc của các loại được yêu cầu hay không, ngay cả khi chính hàm đó không yêu cầu chúng.

Nếu nó được biên dịch thành:

function greet(person) {
    if (!(person instanceof Person))
        throw 'TypeError'

    alert("Hello, " + person.Name);
}

Đó sẽ là một ví dụ về cách gõ danh nghĩa động , bởi vì nó kiểm tra tên của loại đối tượng, không phải cấu trúc của nó.

Tất cả điều này cho thấy rằng gõ không vịt năng động là có thể (cả cấu trúc và danh nghĩa). Nhưng cách tiếp cận này không được sử dụng rất thường xuyên, bởi vì nó chủ yếu kết hợp các nhược điểm của việc gõ không phải vịt (bạn phải xác định rõ ràng các loại; kém linh hoạt) và gõ động (lỗi loại chỉ hiển thị khi chạy và chỉ trong mã thực sự chạy ).

Nếu bạn định thêm chú thích loại để có thể gõ không phải vịt, bạn cũng có thể kiểm tra các loại tại thời gian biên dịch.


1
Tôi sẽ không nói typcript sử dụng kiểu gõ vịt năng động. Thực tế là mặc dù lỗi biên dịch nhưng tsc vẫn tạo ra javascript nghe có vẻ kỳ quặc đối với tôi, nhưng dù sao thì ... lỗi đánh máy đã được báo cáo tại thời điểm biên dịch và phần thời gian chạy được để lại cho javascript, không phải là bản đánh máy. Tuy nhiên, bản thảo sử dụng gõ vịt tĩnh; khi bạn viết "var t = 1;", biến được coi là một số.
Joel

@Joel Có, TypeScript thừa hưởng kiểu gõ động từ JavaScript, nhưng điều đó không làm cho nó trở thành một thuộc tính của TypeScript. Và ví dụ của bạn cho thấy suy luận kiểu không gõ vịt.
Svick

Bạn sẽ gọi một ngôn ngữ yêu cầu các phương thức được xác định là một phần của giao diện, nhưng cho phép các vị trí lưu trữ được nhập bằng cách sử dụng các kết hợp giao diện tùy ý? Nếu chuyển [3,4]sang một số phương thức của một bộ sưu tập giữ [1,2]năng suất [1,2,3,4]và chuyển [3,4]sang một phương thức của một số bộ sưu tập [1,2]nào đó [4,6], liệu các phương thức đó có cùng tên không? Gọi đầu tiên Sequence$Appendable$Addvà thứ hai NumericVector$Addnhưng sau đó có thể nói rằng một biến nên được hiểu là một Sequencehoặc NumericVectorsẽ ...
supercat

... Có vẻ sạch sẽ hơn chỉ đơn giản là hy vọng rằng tên đồng nhất sẽ không gây nhầm lẫn. Trong các ngôn ngữ mà mỗi biến chỉ có một loại, loại "hoàn toàn năng động" có thể hữu ích, nhưng nếu có sẵn loại tổng hợp, tôi sẽ nghĩ rằng sẽ loại bỏ 99% trường hợp sử dụng cho vịt "ngoài luồng" đánh máy.
supercat

Tôi có khuynh hướng đồng ý với @Joel sự khác biệt giữa cảnh báo và ngoại lệ không liên quan nhiều đến việc gõ ngôn ngữ. TypeScript là một thư viện. tsclà một giao diện vào nó. Nếu bạn sử dụng thư viện, nó sẽ kích hoạt một sự kiện. Theo mặc định nếu không có gì nghe bạn nhận được một kịch bản. Nếu bạn nghe và ném một ngoại lệ, bạn có thể ngăn tập lệnh được tạo. Điều đó có thay đổi hệ thống loại của TypeScript không? Dĩ nhiên là không.
Evan Carroll

3

Rõ ràng (từ những gì tôi đọc) Gõ vịt chỉ có ý nghĩa trong ngữ cảnh hướng đối tượng, khi các hàm được gắn dưới dạng phương thức vào đối tượng. Sau đó, khi bạn viết duck.quacks(3), điều này sẽ hoạt động nếu giá trị hiện tại của duckcó phương thức quacks.

Gõ động không nhất thiết phải được gắn vào chế độ xem OO bằng các phương thức.

Bạn có thể xác định loại realvới toán tử hoặc hàm liên quan +: real*real->realvà loại rationalvới toán tử liên quan +: rational*rational->rational. Sau đó, nếu bạn viết a+b, trong một hệ thống thời gian được kiểm tra động, cả hai biến của bạn abcó thể có một +toán tử, nhưng bạn gặp lỗi loại thời gian chạy.

Đánh máy động kiểm tra tính nhất quán của các giá trị, có thể là một vài trong số chúng.

Gõ vịt kiểm tra tính nhất quán hành vi của mã với một đối tượng trong tay (một cái duy nhất, theo như tôi hiểu).

Trong một ý nghĩa, gõ vịt là một dạng đa hình thời gian chạy, ngoại trừ thực tế là nó chỉ áp dụng cho các phương thức truy cập của một đối tượng.

Tuy nhiên, người ta có thể định nghĩa một dạng đa hình thời gian chạy tổng quát hơn, trong đó toán tử +được thực thi sẽ được xác định dựa trên tất cả các đối số. Vì vậy, một con vịt và một con gà có thể danse với nhau nếu chúng có chung một chức năng danse. Họ có thể có một số để vịt có thể danse cũng với ngỗng với một chức năng khác. Nhưng điều đó có vẻ hơi phức tạp. Theo tôi nhớ, một cái gì đó thuộc loại (có lẽ có nhiều cấu trúc hơn) có thể đã có thể với các chức năng chung của ngôn ngữ EL1 , một tiền thân rất cũ của các ngôn ngữ hướng đối tượng.


Python cũng hỗ trợ quá tải toán tử. def add (a, b): trả về a + b là có thể thực hiện được. Nó được chuyển đổi thành .__ thêm __ (b) tự động. ... Vì vậy, yeah, gõ vịt dường như yêu cầu một số loại người dùng xác định nâng cao. Mặc dù các thành viên dữ liệu cũng hoạt động, do đó, cấu trúc vịt cũng có thể, tương tự như thế này: def add ware (a, b, t): t. Ware = a. Ware + b. Ware
StarWeaver

@ StarWeaver Vâng vâng. Nhưng điều làm phiền tôi với ví dụ của bạn là sự đồng điệu giữa các toán hạng. Nếu có một lỗi loại, đó là do a không có phương thức thích hợp hoặc do có một kiểu không khớp giữa các phương thức của a và loại b. Vì vậy, tùy thuộc vào tình huống, bạn sẽ có một ngoại lệ khác (nếu bộ nhớ Python của tôi là chính xác). Tôi có thể muốn một mức độ trừu tượng trong đó tôi sẽ chỉ nhận được một ngoại lệ nói rằng a và b không thể + cùng nhau (xin lỗi vì câu lạ). Nhưng đó có thể là một vấn đề của hương vị. Tôi cũng có thể muốn nói rằng + là giao hoán.
babou

-2

Một câu trả lời bằng cách tương tự:

Bạn có thể mua một chiếc mui trần và không bao giờ đặt từ trên xuống? Chắc chắn rồi. Đây có lẽ không phải là cách tốt nhất để chi tiêu tài nguyên của bạn, vì bạn đã trả thêm tiền cho một số tính năng (ví dụ: đỉnh có thể chuyển đổi, độ cứng kết cấu thêm do thiếu mái như yếu tố cấu trúc) và nhận được một số kết quả tồi tệ hơn (ví dụ như tiếng ồn đường thêm, có thể thấp hơn an toàn sự cố, khoang lưu trữ nhỏ hơn) là kết quả của việc đầu tư vào tính năng đó mà bạn sẽ không sử dụng. Nhưng nó khả thi về mặt kỹ thuật.

Nó giống với ngôn ngữ động và gõ vịt. Bạn đã từ bỏ hiệu quả cao hơn và đảm bảo an toàn loại thời gian biên dịch của các ngôn ngữ tĩnh. Để làm gì? Nói chung cho sự đơn giản của gõ vịt. Các biến và bộ sưu tập có thể chứa bất cứ thứ gì và bạn không cần phải thực hiện nhiều việc chỉ định trước những gì. Các bộ sưu tập hỗn hợp như [ 12, "gumbo", 12.4, 4+4j ](một số nguyên, một chuỗi, một giá trị dấu phẩy động và một giá trị phức tạp) là không đáng kể và bạn không có kiểu truyền liên tục mà bạn thấy trong mã Java (ví dụ).

Có thể sử dụng ngôn ngữ động như Python để tạo các đối tượng không gõ vịt:

class Hungarian(object):
    self __init__(self):
        self.value = []
    self addInt(self, intValue):
        self.value.append(intValue)
    self addFloat(self, floatValue):
        self.value.append(floatValue)
    self addComplex(self, complexValue):
        self.value.append(complexValue)
    # ...

Nhưng như bạn có thể nhận thấy, không có kiểm tra thực tế nào về các loại và mỗi phương thức được thực hiện với cấu trúc tích hợp gõ vịt ( list). Đã trả giá cho sự năng động, bạn cũng có thể đặt từ trên xuống và nhận được sự đơn giản:

class NotHungarian(object):
    def __init__(self):
        self.value = []
    def add(self, whatever):
        self.value.append(whatever)

HungarianLớp nào mà vịt không gõ? Như bạn đã chỉ ra, không có kiểm tra các loại.
Svick

@svick Dự định sẽ được sử dụng theo cách dành riêng cho từng loại, như Java, với các phương thức riêng biệt không phụ thuộc vào hành động được thực hiện mà còn dựa trên các loại được sử dụng. Gõ vịt sẽ sử dụng cùng một phương pháp độc lập với loại được thêm vào NotHungarian. Gõ vịt không chỉ phụ thuộc vào việc không kiểm tra loại mà còn sử dụng cùng tên gọi / phương thức / tên tin nhắn ( add). ( NotHungariancũng sử dụng tên phương thức, addphổ biến với các đối tượng Python khác, chẳng hạn như set. Nó "quacks" giống như các đối tượng / lớp khác.)
Jonathan Eunice

1
Câu trả lời này có một chút sai lệch về động lực thúc đẩy từ bỏ hiệu quả cao hơn và đảm bảo an toàn loại thời gian biên dịch của các ngôn ngữ tĩnh. Động lực là để loại bỏ xiềng xích của hệ thống kiểu Fortran hoặc C, bởi vì các hệ thống này thường gây cản trở (nghĩ về các chuỗi có độ dài cố định của Pascal), ngăn chặn lập trình chung và tạo ra các tính năng cực kỳ mạnh mẽ như evalkhông thể thực hiện được. Động lực không bao giờ là loại bỏ các loại, và một số ngôn ngữ động có gõ gõ dần dần. MJD có một bài thuyết trình hay về gõ tĩnh so với gõ động.
amon

@amon Không đồng ý. Các ngôn ngữ được nhập tĩnh nói chung có một lợi thế khá đáng kể so với các ngôn ngữ được nhập động bởi vì chúng sử dụng các loại giá trị / loại không có hộp, cấu trúc thay vì ký hiệu, v.v. Trình biên dịch JIT đã thu hẹp khoảng cách, nhưng ngôn ngữ được biên dịch tĩnh AOT vẫn nhanh hơn đáng kể. Đó không phải là lý do duy nhất để chọn một ngôn ngữ. Tôi sử dụng động bất cứ lúc nào tôi có thể, và hình phạt hiệu suất bị hạn chế hoặc không quan trọng trong hầu hết thời gian. Và vâng, có những động lực khác để chọn năng động. Nhưng bạn từ bỏ những điều thực sự để đi đến đó.
Jonathan Eunice
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.