Gõ dần: Hầu hết mọi ngôn ngữ có hệ thống kiểu tĩnh cũng có hệ thống kiểu động


20

Yêu cầu này của Aleks Bromfield tuyên bố:

Hầu như mọi ngôn ngữ với hệ thống kiểu tĩnh cũng có hệ thống kiểu động. Ngoài C, tôi không thể nghĩ đến một ngoại lệ

Đây có phải là một yêu cầu hợp lệ? Tôi hiểu rằng với các lớp Reflection hoặc Loading trong thời gian chạy, Java có một chút như thế này - nhưng ý tưởng về 'gõ dần dần' có thể được mở rộng sang một số lượng lớn ngôn ngữ không?


Tôi không phải là chuyên gia ngôn ngữ, nhưng một số ít ngay lập tức nghĩ rằng tôi không tin có kiểu năng động (Về cơ bản, không nói rằng bạn không thể kết hợp một thứ gì đó với nhau trong những thứ này) - Fortran, Ada, Pascal, Cobol
mattnz

4
Tôi là một chuyên gia ngôn ngữ, và tôi không chắc anh chàng này đang tuyên bố gì. "Tĩnh" và "động" là các cách hiểu sai và thường chỉ ra thời gian kiểm tra / phân tích loại (cho dù tại thời gian biên dịch hoặc tại thời gian chạy). Một số ngôn ngữ kết hợp cả hai (ví dụ: bằng cách không cho phép các hoạt động trên các loại không được hỗ trợ trong quá trình biên dịch và đưa ra các ngoại lệ như ClassCastException trong thời gian chạy) và đó có thể là ý nghĩa của nó. Nếu vậy, đúng là C thường không thực hiện kiểm tra kiểu trong thời gian chạy. Nói một lang. không có "hệ thống kiểu động" không có ý nghĩa.
Thiago Silva

Câu trả lời:


37

Tweeter gốc ở đây. :)

Trước hết, tôi có phần thích thú / sốc khi tweet của tôi đang được thực hiện nghiêm túc! Nếu tôi biết nó sẽ được phổ biến rộng rãi như vậy, tôi đã dành hơn 30 giây để viết nó!

Thiago Silva là chính xác để chỉ ra rằng "tĩnh" và "động" mô tả chính xác hơn việc kiểm tra loại , thay vì hệ thống loại . Trên thực tế, thật không chính xác khi nói rằng một ngôn ngữ được gõ tĩnh hoặc động. Thay vào đó, một ngôn ngữ có một hệ thống loại và việc triển khai ngôn ngữ đó có thể thực thi hệ thống loại bằng cách sử dụng kiểm tra tĩnh hoặc kiểm tra động hoặc cả hai hoặc không (mặc dù đó sẽ không phải là một triển khai ngôn ngữ rất hấp dẫn!).

Khi điều đó xảy ra, có một số hệ thống loại (hoặc tính năng của hệ thống loại) dễ kiểm tra tĩnh hơn và có một số hệ thống loại nhất định (hoặc tính năng của hệ thống loại) dễ kiểm tra động hơn. Ví dụ: nếu ngôn ngữ của bạn cho phép bạn chỉ định trong văn bản của chương trình rằng một giá trị cụ thể phải luôn là một mảng các số nguyên, thì việc viết một trình kiểm tra tĩnh để xác minh thuộc tính đó là hợp lý. Ngược lại, nếu ngôn ngữ của bạn có phân nhóm và nếu nó cho phép phát sóng, thì việc kiểm tra tính hợp lệ của một chương trình truyền phát trong thời gian chạy là vô cùng đơn giản, nhưng cực kỳ khó thực hiện trong thời gian biên dịch.

Điều tôi thực sự muốn nói về tweet của mình chỉ đơn giản là phần lớn các triển khai ngôn ngữ thực hiện một số lượng kiểm tra loại động. Hoặc, tương tự, phần lớn các ngôn ngữ có một số tính năng rất khó (nếu không thể) kiểm tra tĩnh. Downcasting là một ví dụ. Các ví dụ khác bao gồm tràn số học, kiểm tra giới hạn mảng và kiểm tra null. Một số trong số này có thể được kiểm tra tĩnh trong một số trường hợp, nhưng nhìn chung, bạn khó có thể tìm thấy một triển khai ngôn ngữ mà không thực hiện bất kỳ kiểm tra nào trong thời gian chạy.

Đây không phải là một điều xấu. Đó chỉ là một quan sát rằng có nhiều đặc tính thú vị mà chúng tôi muốn các ngôn ngữ của chúng tôi thực thi và chúng tôi không thực sự biết cách kiểm tra tĩnh. Và đó là một lời nhắc nhở rằng sự khác biệt như "loại tĩnh" so với "loại động" gần như không rõ ràng như một số người sẽ tin bạn. :)

Một lưu ý cuối cùng: thuật ngữ "mạnh" và "yếu" không thực sự được sử dụng trong cộng đồng nghiên cứu ngôn ngữ lập trình và chúng không thực sự có ý nghĩa nhất quán. Nói chung, tôi thấy rằng khi ai đó nói rằng một ngôn ngữ có "gõ mạnh" và một số ngôn ngữ khác có "gõ yếu", họ thực sự nói rằng ngôn ngữ yêu thích của họ (ngôn ngữ có "gõ mạnh") ngăn họ khỏi mắc một số sai lầm mà ngôn ngữ khác (ngôn ngữ "gõ yếu") không - hoặc ngược lại, ngôn ngữ yêu thích của họ (ngôn ngữ "gõ yếu") cho phép họ thực hiện một số điều thú vị mà ngôn ngữ kia ( một với "gõ mạnh") thì không.


9
"Đó chỉ là một quan sát rằng có nhiều đặc tính thú vị mà chúng tôi muốn các ngôn ngữ của chúng tôi thực thi và chúng tôi không thực sự biết cách kiểm tra tĩnh." - Chà, không chỉ là chúng ta không biết làm thế nào. "Liệu chương trình này dừng lại cho đầu vào này" là một đặc tính thú vị mà chúng ta không chỉ không biết làm thế nào để kiểm tra, nhưng trên thực tế chúng tôi làm bí quyết là bất khả thi để kiểm tra.
Jörg W Mittag 30/12/13

"Các ví dụ khác bao gồm tràn số học, kiểm tra giới hạn mảng và kiểm tra null." Chỉ cần lưu ý rằng mặc dù có một vài ngôn ngữ kiểm tra các thuộc tính này trong hệ thống loại, ngay cả trong hầu hết các ngôn ngữ được nhập động, đây thường là một tính năng của các giá trị , không phải loại. (Kiểm tra "Null" là một tính năng phổ biến của các hệ thống loại tĩnh.)
porglezomp

một loại động hệ thống , và một loại tĩnh hệ thống . Ngay cả khi không được thi hành, chúng vẫn ở đó, mô tả cái gì là đúng và cái gì là lỗi. Ngôn ngữ / việc thực hiện có thể hoặc không thể kiểm tra một trong hai ngôn ngữ đó. BTW, hệ thống loại tĩnh và động phải nhất quán, nhưng nó không phải là trường hợp theo định nghĩa .
Elazar

Mỗi ngôn ngữ có một hệ thống loại động, là một bộ quy tắc cấm các hoạt động nhất định được thực hiện.
Elazar

7

Vâng vâng. Bạn có thể có túi tài sản trong bất kỳ ngôn ngữ gõ tĩnh. Cú pháp sẽ rất tệ trong khi đồng thời bạn sẽ đạt được tất cả các nhược điểm của hệ thống gõ động. Vì vậy, thực sự không có bất kỳ lợi thế nào trừ khi trình biên dịch cho phép bạn sử dụng cú pháp đẹp hơn, một cái gì đó giống như C # dynamicđang làm.

Ngoài ra, bạn cũng có thể làm điều đó khá dễ dàng trong C.

Phản ứng với các câu trả lời khác: Tôi nghĩ mọi người đang nhầm kiểu gõ tĩnh / động với kiểu gõ mạnh / yếu. Gõ động là về việc có thể thay đổi cấu trúc dữ liệu trong thời gian chạy và mã có thể sử dụng dữ liệu phù hợp với những gì mã cần. Đây được gọi là gõ vịt .

Đề cập đến sự phản chiếu là không nói lên toàn bộ câu chuyện, bởi vì sự phản chiếu không cho phép bạn thay đổi cấu trúc dữ liệu hiện có. Bạn không thể thêm trường mới vào một lớp hoặc cấu trúc trong C, C ++, Java hoặc C #. Trong các ngôn ngữ động, việc thêm các trường hoặc thuộc tính mới vào các lớp hiện tại không chỉ có thể, mà thực sự khá phổ biến.

Ví dụ, nhìn vào Cython , trình biên dịch Python-to-C. Nó tạo mã C tĩnh, nhưng hệ thống kiểu vẫn giữ nguyên bản chất động của nó. C là ngôn ngữ gõ tĩnh, nhưng nó có thể hỗ trợ gõ động từ Python.


Về mặt kỹ thuật, bạn có thể thực hiện nó trong C # như phiên bản 4 ExpandoObject, mặc dù đó là quá trình chọn tham gia, không giống như JavaScript hay Ruby. Tuy nhiên, bạn đã đưa ra một điểm rất quan trọng, đó là gõ vịt (điều mà 99% các nhà phát triển thực sự có nghĩa là khi họ nói "gõ động") và phản xạ hoàn toàn không giống nhau.
Aaronaught

Tôi cũng có thể thêm rằng Python không có kiểu gõ vịt thực sự. Nó chỉ có một số móc để bạn "tự thực hiện" (có một phương thức ma thuật có thể quay lại Trueđể nói "đối tượng điên này là một ví dụ của lớp tôi đang xác định"). OCaml có tính năng này theo như tôi hiểu, nhưng tôi không thực sự biết.
vlad-ardelean

6

Ngôn ngữ động là ngôn ngữ tĩnh . Cái thường được gọi là "gõ động" thực sự là một trường hợp đặc biệt của kiểu gõ tĩnh - trường hợp bạn tự giới hạn chỉ có một loại. Là một thử nghiệm tư duy, hãy tưởng tượng viết một chương trình bằng Java hoặc C # chỉ sử dụng Objectcác biến / trường / tham số và truyền xuống ngay lập tức trước khi gọi bất kỳ phương thức nào . Sẽ chính xác hơn khi gọi các ngôn ngữ như Python hoặc Javascript "unityped". (Khiếu nại này có thể sẽ gây nhầm lẫn / làm phiền nhiều người, vì chương trình Java hoặc C # như vậy sẽ sử dụng nhiều loại phụ, nhưng đó là vì ngôn ngữ OOP trung bình kết hợp các loại và lớp. Đọc bài đăng trên blog để biết thêm chi tiết.)

Lưu ý rằng ngay cả C cũng có kiểu gõ "động" - bạn có thể chuyển bất kỳ con trỏ nào sang con trỏ void(và nếu bộ nhớ phục vụ cho tôi, char) và quay lại. Và lưu ý rằng, không có kiểm tra thời gian chạy ở đó; nếu bạn hiểu sai, hãy tận hưởng hành vi không xác định của bạn!


Nhưng các diễn viên được thực hiện tĩnh. Tôi không thấy làm thế nào đây là một ví dụ về gõ động.
osa

@osa Chỉ vì mã nói rằng String foo = (String) barnó không có nghĩa barlà trên thực tế a String. Bạn chỉ có thể biết rằng chắc chắn vào thời gian chạy, vì vậy tôi không thấy cách diễn viên được thực hiện "tĩnh".
Doval

Tôi không đồng ý với điều này. Ít nhất là trong Javascript, bạn có thể thêm các phương thức và thuộc tính mới vào các đối tượng trong thời gian chạy. Trong C #, bạn cần sử dụng rõ ràng một dynamicđối tượng để làm điều này. Nếu bạn cố gắng thêm một tài sản vào một object... tốt, bạn không thể.
Arturo Torres Sánchez

3

Sự khác biệt giữa gõ tĩnh và gõ động là khi loại giá trị được kiểm tra: thời gian biên dịch so với thời gian chạy. Trong các ngôn ngữ mà các giá trị mang theo kiểu của chúng (ví dụ: các đối tượng Java), thì bạn luôn có thể sử dụng kiểu gõ động ngay cả khi ngôn ngữ thực sự thích gõ tĩnh. Đây là một ví dụ trong Java, với một phương thức gõ động:

void horribleJava(List untypedList) {
  for (Object o : untypedList)
    ((SpecificType) o).frobnicate();
}

Lưu ý cách loại của từng mục được kiểm tra trong thời gian chạy. Phương thức gõ tĩnh tương đương là:

void goodJava(List<SpecificType> typedList) {
  for (SpecificType o : typedList) {
    o.forbnicate();
  }
}

Trong C, các giá trị (và cụ thể là con trỏ) không giữ lại kiểu của chúng trong thời gian chạy - mọi con trỏ đều tương đương với a void *. Thay vào đó, các biến và biểu thức có một loại. Để đạt được kiểu gõ động, bạn phải tự mang theo thông tin loại (ví dụ như một trường trong cấu trúc).


1
Tôi không nghĩ rằng một diễn viên được tính là gõ động theo bất kỳ ý nghĩa thực tế nào. Nó vẫn đòi hỏi kiến ​​thức về kiểu lúc biên dịch, nó chỉ trì hoãn xác nhận thực tế cho đến khi thực thi. Bạn không thể gọi frobnicatephương thức ở đây mà không biết trước SpecificType.
Aaronaught

2
@Aaronaught Nhưng đây kiểu gõ động một khi chúng tôi đã hoãn kiểm tra loại để chạy thời gian. Lưu ý rằng cả hai ví dụ của tôi đều sử dụng kiểu gõ chỉ định yêu cầu tên loại cụ thể. Bạn đang nghĩ đến việc gõ cấu trúc , ví dụ gõ vịt đòi hỏi phải có sự hiện diện của một số phương pháp. Các trục cấu trúc so với chỉ định và gõ tĩnh so với gõ động là trực giao với nhau (Java là chỉ định và chủ yếu là tĩnh; ML và Go là cấu trúc và tĩnh; Perl, Python và Ruby (chủ yếu) là cấu trúc và động).
amon

Giống như tôi đã nói trong bình luận cuối cùng của tôi, đó là một sự khác biệt về mặt lý thuyết mà không có lập trình viên nào tôi từng gặp thực sự quan tâm. Bạn có thể tranh luận một cách thô bạo rằng mọi người đang sử dụng thuật ngữ sai (và thực tế có vẻ như tweeter ban đầu sẽ chính xác là loại ngu ngốc đó) nhưng đối với những người thực sự ở trong rãnh, gõ động = gõ vịt. Trong thực tế, định nghĩa rất phổ biến đến nỗi những ngôn ngữ như C # đã thực sự mã hóa nó (nghĩa là với dynamictừ khóa). Tương đương tĩnh với thời gian biên dịchđộng đối với thời gian chạy chủ yếu chỉ làm vẩn đục nước.
Aaronaught

@Aaronaught: Nếu một người chuyển sang một giao diện và nếu các lớp thực hiện một phương thức với ý nghĩa dự định luôn thực hiện cùng một giao diện để nói như vậy, thì về cơ bản, đó sẽ là kiểu gõ vịt thời gian chạy. Mặc dù một số người có thể lập luận rằng "gõ vịt" chỉ nên sử dụng tên phương thức, tôi nghĩ sẽ hữu ích hơn khi coi tên phương thức "thực" là tên giao diện cộng với tên phương thức. Nếu không, nên [1,2].Add([3,4])năng suất [1,2,3,4], [1,2,[3,4]]hoặc [4,6]?
supercat

@supercat: Không có điều nào ở trên, nó sẽ thất bại vì không có Addphương thức nào trên mảng chấp nhận một mảng vì phương thức như vậy sẽ mơ hồ. Gõ vịt không tha cho bạn viết các loại và chức năng dễ hiểu.
Aaronaught

2

Gõ tĩnh so với động cơ bản đề cập đến cách kiểm tra các loại. Gõ tĩnh có nghĩa là việc xác minh các loại biến hoặc biểu thức khác nhau được kiểm tra dựa trên mã thực tế (thường là bởi trình biên dịch) trong khi trong hệ thống loại động, việc xác minh này chỉ được thực hiện trong thời gian chạy, bởi môi trường thời gian chạy.

Những gì tôi tin rằng văn bản được đề cập là ngay cả khi các loại thực sự được kiểm tra tĩnh, chúng cũng được kiểm tra trong thời gian chạy, tức là động. Bạn đã đề cập chính xác Java Reflection; sự phản chiếu chỉ xảy ra trong thời gian chạy và Môi trường chạy thi hành Java (JVM) thực sự thực hiện kiểm tra kiểu khi sử dụng sự phản chiếu, về cơ bản có nghĩa là kiểm tra kiểu động.


Vậy thì không đúng. Trong C ++, Pascal, Fortran --- trong bất kỳ ngôn ngữ được biên dịch nào --- các loại chỉ được kiểm tra tĩnh và không động (trừ khi bạn đang sử dụng Dynamic_cast). Bạn có thể sử dụng các con trỏ để ghi các nội dung tùy ý vào bộ nhớ và không ai sẽ biết các loại. Vì vậy, gõ tĩnh có nghĩa là CHỈ KIỂM TRA TÌNH TRẠNG, trong khi gõ động có nghĩa là CHỈ KIỂM TRA TẠI RUNTIME.
osa

-1

Phần trích dẫn là sai: C cũng có một hệ thống loại động quan trọng. Nó chỉ đơn giản là không kiểm tra nó ("C được gõ mạnh, kiểm tra yếu"). Ví dụ, xử lý một cấu trúc như một double( reinternpret_castkiểu) mang lại hành vi không xác định - một lỗi loại động.


(Để ai downvoted - nhận xét @Thiago Silva cho biết về điều tương tự)
Elazar
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.