Một hệ thống loại là gì?


50

Lý lịch

Tôi đang thiết kế một ngôn ngữ, như một dự án phụ. Tôi có một trình biên dịch làm việc, phân tích tĩnh và máy ảo cho nó. Vì tôi đã có thể biên dịch và chạy các chương trình không tầm thường bằng cách sử dụng cơ sở hạ tầng mà tôi đã xây dựng nên tôi nghĩ về việc thuyết trình tại trường đại học của mình.

Trong cuộc nói chuyện của tôi, tôi đã đề cập rằng VM cung cấp một hệ thống loại, được hỏi " Hệ thống loại của bạn để làm gì? ". Sau khi trả lời tôi đã cười nhạo người đặt câu hỏi.

Vì vậy, mặc dù tôi gần như chắc chắn sẽ mất danh tiếng khi đặt câu hỏi này, tôi chuyển sang lập trình viên.

Sự hiểu biết của tôi

Theo tôi hiểu, các hệ thống loại được sử dụng để cung cấp lớp thông tin bổ sung về các thực thể trong chương trình, để bộ thực thi hoặc trình biên dịch hoặc bất kỳ phần máy móc nào khác biết phải làm gì với chuỗi bit mà nó hoạt động. Chúng cũng giúp duy trì các hợp đồng - trình biên dịch (hoặc trình phân tích mã hoặc thời gian chạy hoặc bất kỳ chương trình nào khác) có thể xác minh rằng tại bất kỳ thời điểm nào, chương trình hoạt động dựa trên các giá trị mà các lập trình viên mong muốn nó hoạt động.

Các loại cũng có thể được sử dụng để cung cấp thông tin cho những lập trình viên con người. Ví dụ, tôi tìm thấy tuyên bố này:

function sqrt(double n) -> double;

hữu ích hơn cái này

sqrt(n)

Cái trước cung cấp nhiều thông tin: rằng sqrtđịnh danh là một hàm, lấy một cái doublelàm đầu vào và tạo ra cái khác doublelàm đầu ra. Cái sau cho bạn biết rằng nó có thể là một hàm lấy một tham số duy nhất.

Câu trả lời của tôi

Vì vậy, sau khi được hỏi "Hệ thống loại của bạn để làm gì?" Tôi đã trả lời như sau:

Hệ thống loại là động (các loại được gán cho các giá trị, không phải cho các biến giữ chúng), nhưng mạnh mẽ mà không có quy tắc cưỡng chế đáng ngạc nhiên (bạn không thể thêm chuỗi vào số nguyên vì chúng đại diện cho các loại không tương thích, nhưng bạn có thể thêm số nguyên vào số dấu phẩy động) .

Hệ thống loại được VM sử dụng để đảm bảo các toán hạng cho các hướng dẫn là hợp lệ; và có thể được sử dụng bởi các lập trình viên để đảm bảo rằng các tham số được truyền cho các hàm của họ là hợp lệ (nghĩa là đúng loại).
Hệ thống loại hỗ trợ phân nhóm và nhiều kế thừa (cả hai tính năng đều có sẵn cho các lập trình viên) và các loại được xem xét khi gửi phương thức động trên các đối tượng được sử dụng - VM sử dụng các loại để kiểm tra xem chức năng nào là thông báo đã cho được thực hiện cho loại đã cho.

Câu hỏi tiếp theo là "Và loại được gán cho một giá trị như thế nào?". Vì vậy, tôi đã giải thích rằng tất cả các giá trị được đóng hộp và có một con trỏ trỏ đến cấu trúc định nghĩa kiểu cung cấp thông tin về tên của loại, thông điệp mà nó phản hồi và loại nào được thừa hưởng.

Sau đó, tôi đã bật cười và câu trả lời của tôi đã bị bác bỏ với nhận xét "Đó không phải là một hệ thống kiểu thực sự."

Vậy - nếu những gì tôi mô tả không đủ điều kiện là một "hệ thống kiểu thực", thì điều gì sẽ xảy ra? Người đó có đúng không khi những gì tôi cung cấp không thể được coi là một hệ thống kiểu?


19
Khi mọi người nói về các hệ thống loại, họ thường nói về gõ tĩnh. Kiểu gõ động không thú vị lắm đối với loại người quan tâm đến hệ thống loại vì nó đảm bảo hầu như không có gì. Ví dụ: loại giá trị nào có thể biến x giữ? Bất cứ điều gì.
Doval

7
Tôi tò mò muốn nghe những gì họ nói để bảo vệ / giải thích phản ứng của họ.
Newtopian

18
@Doval Gõ động có thể đảm bảo bạn không rơi vào trạng thái vô nghĩa bằng cách làm gì đó như thêm 5 vào con mèo của bạn. Chắc chắn, điều đó sẽ không ngăn bạn cố gắng , nhưng ít nhất nó có thể ngăn nó thực sự xảy ra và cho bạn cơ hội tìm ra điều gì sai và thực hiện các hành động khắc phục, những điều mà một ngôn ngữ thực sự không thể đánh máy được.
8bittree

10
Người đã đưa ra vấn đề với câu trả lời của bạn cho "Và loại được gán cho một giá trị như thế nào?". Họ muốn nghe về các quy tắc gõ, không phải sơ đồ hộp và con trỏ. Cười là hoàn toàn thô lỗ, mặc dù.
vườn

10
Người hay cười rất có thể là người nhiệt tình đối với một số ngôn ngữ cụ thể (gia đình) với hệ thống loại mạnh (Haskell có vẻ phổ biến), và sẽ chế giễu bất cứ thứ gì kém mạnh mẽ (và do đó là một món đồ chơi) hơn, hoặc mạnh mẽ hơn (và do đó không thực tế), hoặc chỉ khác nhau. Tham gia thảo luận với những người quá khích là nguy hiểm và vô ích. Cười như thế chỉ là thô lỗ đến nỗi nó chỉ ra loại vấn đề sâu sắc hơn. Bạn thật may mắn khi họ không bắt đầu rao giảng ...
hyde

Câu trả lời:


30

Đó là tất cả giống như một mô tả tốt về những gì hệ thống loại cung cấp. Và việc thực hiện của bạn nghe có vẻ hợp lý cho những gì nó đang làm.

Đối với một số ngôn ngữ, bạn sẽ không cần thông tin về thời gian chạy vì ngôn ngữ của bạn không thực hiện gửi thời gian chạy (hoặc bạn thực hiện gửi một lần qua vtables hoặc cơ chế khác, vì vậy không cần thông tin loại). Đối với một số ngôn ngữ, chỉ cần có ký hiệu / trình giữ chỗ là đủ vì bạn chỉ quan tâm đến sự bình đẳng loại, không phải tên hoặc thừa kế của nó.

Tùy thuộc vào môi trường của bạn, người đó có thể muốn có nhiều hình thức hơn trong hệ thống loại của bạn. Họ muốn biết những gì bạn có thể chứng minh với nó, chứ không phải những gì lập trình viên có thể làm với nó. Điều này là khá phổ biến trong học viện không may. Mặc dù các học giả làm những việc như vậy bởi vì nó khá dễ có sai sót trong hệ thống loại của bạn cho phép mọi thứ thoát khỏi sự chính xác. Có thể họ phát hiện ra một trong số này.

Nếu bạn có thêm câu hỏi, Các loại và Ngôn ngữ lập trình là cuốn sách kinh điển về chủ đề này và có thể giúp bạn tìm hiểu một số sự khắt khe cần thiết của các học giả, cũng như một số thuật ngữ để giúp mô tả mọi thứ.


3
"Tùy thuộc vào môi trường của bạn, người đó có thể muốn có nhiều hình thức hơn trong hệ thống loại của bạn." Đó có lẽ là nó. Tôi đã không tập trung vào những gì tôi có thể chứng minh với hệ thống loại, mà chỉ nghĩ về nó như một công cụ. Cảm ơn vì giới thiệu quyển sách!
Mael

1
@Mael Một số hệ thống loại được sử dụng làm logic (xem khung logic ). vì vậy về cơ bản, kiểu này cung cấp cho các công thức và các chương trình là bằng chứng của các công thức đó (ví dụ: loại hàm a -> bcó thể được xem là hàm ý b , tức là nếu bạn cho tôi một giá trị của loại atôi có thể nhận được giá trị của loại b). Tuy nhiên, để điều này phù hợp, ngôn ngữ phải hoàn toàn và do đó không hoàn thành Turing. Vì vậy, tất cả các hệ thống loại thực tế thực sự xác định một logic không nhất quán.
Bakuriu

20

Tôi thích câu trả lời của @ Telastyn đặc biệt vì liên quan đến mối quan tâm học thuật trong chủ nghĩa hình thức.

Cho phép tôi thêm vào cuộc thảo luận.

Một hệ thống loại là gì?

Một hệ thống loại là một cơ chế để xác định, phát hiện và ngăn chặn các trạng thái chương trình bất hợp pháp. Nó hoạt động bằng cách xác định và áp dụng các ràng buộc. Các định nghĩa ràng buộc là các loại và, các ứng dụng ràng buộc là các cách sử dụng các loại , ví dụ như trong khai báo biến.

Các định nghĩa kiểu thường hỗ trợ các toán tử thành phần (ví dụ: các dạng kết hợp khác nhau, như trong các cấu trúc, phân lớp và phân tách, như trong enums, unions).

Các ràng buộc, cách sử dụng của các loại, đôi khi cũng cho phép các toán tử thành phần (ví dụ: ít nhất là cái này, chính xác cái này, cái này hoặc cái kia, cái này cung cấp cái gì đó khác).

Nếu hệ thống loại có sẵn bằng ngôn ngữ và được áp dụng tại thời điểm biên dịch hướng tới mục tiêu có thể đưa ra lỗi thời gian biên dịch, thì đó là hệ thống loại tĩnh; những điều này ngăn cản nhiều chương trình bất hợp pháp biên dịch, hãy để một mình chạy, do đó nó ngăn chặn các trạng thái chương trình bất hợp pháp.

. và đánh giá chương trình bị lỗi trước khi nó chạy.)

Nếu một hệ thống loại được áp dụng trong thời gian chạy, thì đó là một hệ thống loại động ngăn chặn các trạng thái chương trình bất hợp pháp: nhưng bằng cách dừng chương trình ở giữa chạy, thay vì ngăn không cho nó chạy ở nơi đầu tiên.

Một hệ thống cung cấp loại khá phổ biến là cung cấp cả tính năng tĩnh và động.


Tôi không nghĩ cái gọi là hệ thống kiểu lai là rất phổ biến. Những ngôn ngữ bạn có trong tâm trí?
vườn

2
@gardenhead, khả năng downcast không phải là một tính năng hệ thống kiểu tĩnh, do đó, nó thường được kiểm tra một cách linh hoạt trong thời gian chạy.
Erik Eidt

1
@gardenhead: hầu hết các ngôn ngữ được nhập tĩnh cho phép bạn trì hoãn việc nhập vào thời gian chạy, chỉ đơn giản là với các void *con trỏ của C (rất yếu), các đối tượng động của C # hoặc các GADT được định lượng hiện tại của Haskell ngôn ngữ).
leftaroundabout

Đúng, tôi đã quên "đúc". Nhưng đúc chỉ là một cái nạng cho một hệ thống loại yếu.
vườn

@gardenhead Cũng như các ngôn ngữ tĩnh cung cấp tùy chọn động, nhiều ngôn ngữ động cung cấp một số kiểu gõ tĩnh. Chẳng hạn, Dart, Python và Hack, tất cả đều có các chế độ hoặc công cụ để thực hiện phân tích tĩnh dựa trên khái niệm "gõ dần dần".
IMSoP

14

Ôi trời, tôi rất phấn khích khi cố gắng trả lời câu hỏi này tốt nhất có thể. Tôi hy vọng tôi có thể có được suy nghĩ của tôi đúng thứ tự.

Như @Doval đã đề cập và người hỏi đã chỉ ra (mặc dù thô lỗ), bạn không thực sự có một hệ thống loại. Bạn có một hệ thống kiểm tra động bằng cách sử dụng các thẻ, nói chung là yếu hơn nhiều, và cũng ít thú vị hơn nhiều.

Câu hỏi về "hệ thống kiểu gì" có thể khá triết lý và chúng ta có thể điền vào một cuốn sách với các quan điểm khác nhau về vấn đề này. Tuy nhiên, vì đây là trang dành cho lập trình viên, tôi sẽ cố gắng giữ câu trả lời của mình thực tế nhất có thể (và thực sự, các loại cực kỳ thực tế trong lập trình, mặc dù một số người có thể nghĩ).

Tổng quan

Hãy bắt đầu với một chiếc quần lót để hiểu hệ thống loại nào tốt cho việc này, trước khi đi sâu vào nền tảng chính thức hơn. Một hệ thống loại áp đặt cấu trúc trên các chương trình của chúng tôi . Chúng cho chúng ta biết làm thế nào chúng ta có thể cắm các chức năng và biểu thức khác nhau lại với nhau. Không có cấu trúc, các chương trình là không thể bảo vệ và cực kỳ phức tạp, sẵn sàng gây ra tác hại ở một lỗi nhỏ nhất của lập trình viên.

Viết chương trình với hệ thống kiểu giống như lái xe trong điều kiện đúc tiền - phanh hoạt động, cửa đóng an toàn, động cơ được bôi dầu, v.v. Viết chương trình không có hệ thống kiểu giống như đi xe máy không đội mũ bảo hiểm và làm bánh xe ra khỏi spaghetti. Bạn hoàn toàn không kiểm soát được bạn.

Để thảo luận, chúng ta hãy nói rằng chúng ta có một ngôn ngữ với biểu thức bằng chữ num[n]str[s]đại diện cho chữ số n và chuỗi s, tương ứng, và các hàm nguyên thủy plusconcat, với ý nghĩa dự định. Rõ ràng, bạn không muốn có thể viết một cái gì đó như plus "hello" "world"hoặc concat 2 4. Nhưng làm thế nào chúng ta có thể ngăn chặn điều này? Một tiên nghiệm , không có phương pháp để phân biệt chữ số 2 với chuỗi "thế giới" theo nghĩa đen. Điều chúng tôi muốn nói là những biểu thức này nên được sử dụng trong các bối cảnh khác nhau; họ có nhiều loại khác nhau

Ngôn ngữ và các loại

Hãy lùi lại một chút: ngôn ngữ lập trình là gì? Nói chung, chúng ta có thể chia một ngôn ngữ lập trình thành hai lớp: cú pháp và ngữ nghĩa. Chúng cũng được gọi là staticsđộng lực tương ứng. Nó chỉ ra rằng hệ thống loại là cần thiết để trung gian tương tác giữa hai phần này.

Cú pháp

Một chương trình là một cái cây. Đừng để bị lừa bởi những dòng văn bản bạn viết trên máy tính; đây chỉ là những đại diện có thể đọc được của con người trong một chương trình. Chương trình này là một Cây Cú pháp Trừu tượng . Ví dụ: trong C chúng ta có thể viết:

int square(int x) { 
    return x * x;
 }

Đó là cú pháp cụ thể cho chương trình (đoạn). Đại diện của cây là:

     function square
     /     |       \
   int   int x    return
                     |
                   times
                  /    \
                 x      x

Một ngôn ngữ lập trình cung cấp một ngữ pháp xác định các cây hợp lệ của ngôn ngữ đó (có thể sử dụng cú pháp cụ thể hoặc trừu tượng). Điều này thường được thực hiện bằng cách sử dụng một cái gì đó như ký hiệu BNF. Tôi cho rằng bạn đã làm điều này cho ngôn ngữ bạn đã tạo.

Ngữ nghĩa

OK, chúng tôi biết chương trình là gì, nhưng nó chỉ là một cấu trúc cây tĩnh. Có lẽ, chúng tôi muốn chương trình của chúng tôi thực sự tính toán một cái gì đó. Chúng tôi cần ngữ nghĩa.

Ngữ nghĩa của ngôn ngữ lập trình là một lĩnh vực nghiên cứu phong phú. Nói rộng ra, có hai cách tiếp cận: ngữ nghĩa họcngữ nghĩa hoạt động . Ngữ nghĩa học biểu thị mô tả một chương trình bằng cách ánh xạ nó vào một số cấu trúc toán học cơ bản (ví dụ: số tự nhiên, hàm liên tục, v.v.). cung cấp ý nghĩa cho chương trình của chúng tôi. Ngược lại, ngữ nghĩa hoạt động, định nghĩa một chương trình bằng cách chi tiết cách thức thực thi. Theo tôi, ngữ nghĩa hoạt động trực quan hơn đối với các lập trình viên (bao gồm cả bản thân tôi), vì vậy hãy gắn bó với điều đó.

Tôi sẽ không tìm hiểu cách xác định ngữ nghĩa hoạt động chính thức (các chi tiết có một chút liên quan), nhưng về cơ bản, chúng tôi muốn các quy tắc như sau:

  1. num[n] là một giá trị
  2. str[s] là một giá trị
  3. Nếu num[n1]và ước tính num[n2]cho các số nguyên n_1$ and $n_2$, thencộng (num [n1], num [n2]) `ước tính cho số nguyên $ n_1 + n_2 $.
  4. Nếu str[s1]str[s2]ước lượng cho chuỗi s1 và s2, sau đó concat(str[s1], str[s2])ước tính cho chuỗi s1s2.

V.v. Các quy tắc trong thực tế chính thức hơn rất nhiều, nhưng bạn có được ý chính. Tuy nhiên, chúng tôi sớm gặp phải một vấn đề. Điều gì xảy ra khi chúng ta viết như sau:

concat(num[5], str[hello])

Hừm. Đây là một câu hỏi hóc búa. Chúng tôi chưa định nghĩa một quy tắc ở bất cứ đâu về cách nối một số với một chuỗi. Chúng tôi có thể cố gắng tạo ra một quy tắc như vậy, nhưng theo trực giác chúng tôi biết rằng hoạt động này là vô nghĩa. Chúng tôi không muốn chương trình này có hiệu lực. Và do đó, chúng tôi được dẫn đến các loại.

Các loại

Chương trình là một cái cây như được định nghĩa bởi ngữ pháp của ngôn ngữ. Các chương trình được đưa ra ý nghĩa bởi các quy tắc thực hiện. Nhưng một số chương trình không thể được thực thi; có nghĩa là, một số chương trình là vô nghĩa . Các chương trình này được đánh máy sai. Do đó, gõ đặc trưng cho các chương trình có ý nghĩa trong một ngôn ngữ. Nếu một chương trình được gõ tốt, chúng ta có thể thực hiện nó.

Hãy cho một số ví dụ. Một lần nữa, như với các quy tắc đánh giá, tôi sẽ trình bày các quy tắc đánh máy không chính thức, nhưng chúng có thể được thực hiện nghiêm ngặt. Dưới đây là một số quy tắc:

  1. Mã thông báo của biểu mẫu num[n]có loại nat.
  2. Mã thông báo của biểu mẫu str[s]có loại str.
  3. Nếu biểu thức e1có kiểu natvà biểu thức e2có kiểu nat, thì biểu thức plus(e1, e2)có kiểu nat.
  4. Nếu biểu thức e1có kiểu strvà biểu thức e2có kiểu str, thì biểu thức concat(e1, e2)có kiểu str.

Vì vậy, theo các quy tắc này, có plus(num[5], num[2])loại nat, nhưng chúng ta không thể gán loại cho plus(num[5], str["hello"]). Chúng tôi nói rằng một chương trình (hoặc biểu thức) được gõ tốt nếu chúng tôi có thể gán cho nó bất kỳ loại nào và nó được gõ sai. Một hệ thống loại là âm thanh nếu tất cả các chương trình gõ tốt có thể được thực thi. Haskell là âm thanh; C thì không.

Phần kết luận

Có quan điểm khác về các loại. Các loại trong một số ý nghĩa tương ứng với logic trực giác, và chúng cũng có thể được xem như là đối tượng trong lý thuyết thể loại. Hiểu các kết nối này là hấp dẫn, nhưng nó không cần thiết nếu người ta chỉ muốn viết hoặc thậm chí thiết kế một ngôn ngữ lập trình. Tuy nhiên, hiểu các loại như một công cụ để kiểm soát sự hình thành chương trình là điều cần thiết để thiết kế và phát triển ngôn ngữ lập trình. Tôi chỉ trầy xước bề mặt của những loại có thể thể hiện. Tôi hy vọng bạn nghĩ rằng chúng đủ giá trị để kết hợp với ngôn ngữ của bạn.


4
+1. Thủ thuật lớn nhất mà những người đam mê đánh máy năng động từng lôi kéo là thuyết phục thế giới bạn có thể có "loại" mà không cần hệ thống loại. :-)
ruakh

1
bạn không thể tự động xác minh bất cứ điều gì thú vị cho các chương trình tùy ý, mọi hệ thống loại phải cung cấp một toán tử đúc (hoặc tương đương đạo đức), nếu không, nó sẽ hy sinh tính hoàn chỉnh của Turing. Điều này bao gồm Haskell , tất nhiên.
Kevin

1
@Kevin Tôi biết rõ về định lý của Rice, nhưng nó không liên quan như bạn nghĩ. Để bắt đầu, phần lớn các chương trình không yêu cầu đệ quy không giới hạn. Nếu chúng tôi làm việc trong một ngôn ngữ chỉ có đệ quy nguyên thủy, chẳng hạn như Hệ thống T của Godel, thì chúng tôi có thể xác minh các thuộc tính thú vị bằng cách sử dụng một hệ thống loại, bao gồm cả tạm dừng. Hầu hết các chương trình trong thế giới thực khá đơn giản - tôi không thể nghĩ đến lần cuối cùng tôi thực sự có nhu cầu casting. Turing đầy đủ được đánh giá cao.
vườn

9
Tiếng gõ động của gõ không thực sự gõ đối với tôi như các nhạc sĩ cổ điển nói rằng nhạc Pop không thực sự là nhạc, hay các nhà truyền giáo nói rằng người Công giáo không thực sự là Kitô hữu. Vâng, hệ thống kiểu tĩnh là mạnh mẽ và hấp dẫn và quan trọng, và gõ động là một cái gì đó khác nhau. Nhưng (như các câu trả lời khác mô tả) có một loạt những điều hữu ích ngoài các hệ thống kiểu tĩnh được gọi là gõ, và tất cả đều có chung những điểm chung quan trọng. Tại sao cần phải nhấn mạnh Loại gõ của chúng tôi là Kiểu gõ đúng?
Peter LeFanu Lumsdaine

5
@IMSoP: đối với một cái gì đó ngắn hơn một cuốn sách, bài tiểu luận của Chris Smith Những điều cần biết trước khi tranh luận về các hệ thống loại là tuyệt vời, đặt ra lý do tại sao gõ động thực sự khác với gõ tĩnh.
Peter LeFanu Lumsdaine
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.