Thuật ngữ chính xác trong lý thuyết loại: loại, hàm tạo kiểu, loại / loại và giá trị


14

Trong một câu trả lời cho một câu hỏi trước đó , một cuộc tranh luận nhỏ đã bắt đầu về thuật ngữ chính xác cho các cấu trúc nhất định. Như tôi đã không tìm thấy một câu hỏi (trừ này hay rằng , đó không hẳn là điều đúng đắn) để giải quyết điều này rõ ràng, tôi đang làm cái mới này.

Các thuật ngữ nghi vấn và mối quan hệ của chúng là: loại, hàm tạo kiểu, tham số kiểu, loại hoặc sắp xếp và giá trị .

Tôi cũng đã kiểm tra wikipedia về lý thuyết loại, nhưng điều đó cũng không làm rõ nó nhiều.

Vì vậy, để có một câu trả lời tham khảo tốt và kiểm tra sự hiểu biết của riêng tôi:

  • Làm thế nào những điều này được định nghĩa đúng?
  • Sự khác biệt giữa mỗi thứ này là gì?
  • Chúng liên quan với nhau như thế nào?

Câu trả lời:


13

Được rồi, chúng ta hãy đi từng người một.

Giá trị

Giá trị là những phần cụ thể của dữ liệu mà các chương trình đánh giá và tung hứng. Không có gì lạ mắt, một số ví dụ có thể là

  • 1
  • true
  • "fizz buzz foo bar"

Các loại

Một mô tả hay cho một loại là "phân loại cho một giá trị". Một loại là một ít thông tin về giá trị đó sẽ là gì trong thời gian chạy, nhưng được chỉ định tại thời gian biên dịch.

Ví dụ: nếu bạn nói với tôi rằng e : boolvào thời gian biên dịch, và tôi sẽ biết đó etruehoặcfalse trong thời gian chạy, không có gì khác! Vì các loại phân loại các giá trị độc đáo như thế này, chúng tôi có thể sử dụng thông tin này để xác định một số thuộc tính cơ bản của chương trình của bạn.

Ví dụ, nếu tôi từng thấy bạn thêm ee'khi nào e : inte' : String , sau đó tôi biết một cái gì đó là một chút đi! Trong thực tế, tôi có thể gắn cờ này và đưa ra một lỗi trong thời gian biên dịch, nói rằng "Này, điều đó không có ý nghĩa gì cả!".

Một hệ thống loại mạnh hơn cho phép các loại thú vị hơn phân loại các giá trị thú vị hơn. Ví dụ: hãy xem xét một số chức năng

f = fun x -> x

Điều đó khá rõ ràng f : Something -> Something, nhưng đó phải Somethinglà gì? Trong một hệ thống kiểu nhàm chán, chúng ta sẽ phải chỉ định một cái gì đó tùy ý, như thế nào Something = int. Trong một hệ thống loại linh hoạt hơn, chúng ta có thể nói

f : forall a. a -> a

Đó là nói "cho bất kỳ a, fánh xạ atới một a". Điều này cho phép chúng tôi sử dụng fchung hơn và viết các chương trình thú vị hơn.

Hơn nữa, trình biên dịch sẽ kiểm tra thực sự thỏa mãn trình phân loại mà chúng tôi đã đưa ra, nếu f = fun x -> truesau đó chúng tôi có một lỗi và trình biên dịch sẽ nói như vậy!

Vì vậy, như một tldr; một kiểu là một ràng buộc thời gian biên dịch trên các giá trị mà một biểu thức có thể có trong thời gian chạy.

Kiểu xây dựng

Một số loại có liên quan. Ví dụ, một danh sách các số nguyên rất giống với danh sách các chuỗi. Điều này gần giống như cách sortcho số nguyên gần giống nhưsort đối với chuỗi. Chúng ta có thể tưởng tượng một loại nhà máy xây dựng các loại gần giống nhau này bằng cách khái quát hóa sự khác biệt của chúng và xây dựng chúng theo yêu cầu. Đó là những gì một nhà xây dựng kiểu. Nó giống như một chức năng từ loại đến loại, nhưng hạn chế hơn một chút.

Ví dụ cổ điển là một danh sách chung. Một constructor kiểu chỉ là định nghĩa chung

 data List a = Cons a (List a) | Nil

Bây giờ Listlà một hàm ánh xạ một loại avào danh sách các giá trị của loại đó! Ở vùng đất Java tôi nghĩ có lẽ chúng được gọi là "các lớp chung"

Loại tham số

Tham số kiểu chỉ là kiểu được truyền cho hàm tạo kiểu (hoặc hàm). Giống như ở mức giá trị, chúng ta sẽ nói foo(a)có một tham số agiống như cách List acó một tham số loại a.

Các loại

Các loại là một chút khó khăn. Ý tưởng cơ bản là một số loại tương tự nhau. Ví dụ, chúng tôi có tất cả các loại nguyên thủy trong java int, char, float... mà tất cả cư xử như thể họ có cùng một "loại". Ngoại trừ, khi chúng ta nói về các trình phân loại cho chính các loại, chúng ta gọi các loại phân loại. Vì vậy int : Prim, String : Box, List : Boxed -> Boxed.

Hệ thống này đưa ra các quy tắc cụ thể tốt đẹp về loại loại chúng ta có thể sử dụng ở đâu, giống như cách các loại chi phối các giá trị. Rõ ràng là vô nghĩa khi nói

 List<List>

hoặc là

 List<int>

Trong Java vì Listcần phải được áp dụng cho một loại cụ thể để được sử dụng như thế! Nếu chúng ta nhìn vào các loại của họ List : Boxed -> Boxedvà kể từ Boxed -> Boxed /= Boxedtrên, đây là một lỗi loại!

Hầu hết thời gian chúng ta không thực sự nghĩ về các loại và chỉ coi chúng là "lẽ thường", nhưng với các hệ thống kiểu fancier thì đó là điều quan trọng để suy nghĩ.

Một minh họa nhỏ về những gì tôi đã nói cho đến nay

 value   : type : kind  : ...
 true    : bool : Prim  : ...
 new F() : Foo  : Boxed : ...

Đọc tốt hơn Wikipedia

Nếu bạn quan tâm đến loại điều này, tôi khuyên bạn nên đầu tư một cuốn sách giáo khoa tốt. Lý thuyết loại và PLT nói chung là khá rộng lớn và không có nền tảng kiến ​​thức mạch lạc, bạn (hoặc ít nhất là tôi) có thể đi lang thang mà không cần đi đâu trong nhiều tháng.

Hai cuốn sách yêu thích của tôi là

  • Các loại và ngôn ngữ lập trình - Ben Pierce
  • Cơ sở thực tiễn của ngôn ngữ lập trình - Bob Harper

Cả hai đều là những cuốn sách tuyệt vời giới thiệu những gì tôi vừa nói và nhiều chi tiết đẹp, được giải thích rõ ràng.


1
Các loại là bộ? Tôi thích "phân loại" tốt hơn, nhưng bạn không giải thích điều này có nghĩa là gì và không hiểu rõ về loại này là gì, phần còn lại của câu trả lời của bạn rơi xuống.
Robert Harvey

@RobertHarvey Bây giờ trông nó thế nào, tôi đã bỏ tất cả đề cập đến các bộ :)
Daniel Gratzer

1
Tốt hơn nhiều ....
Robert Harvey

@RobertHarvey Tôi thấy chế độ xem các bộ là rất trực quan. Ví dụ: Kiểu inttrong Java bao gồm một tập hợp 2 ^ 64 giá trị riêng biệt. Sự tương tự với các tập hợp bị phá vỡ khi các kiểu con tham gia, nhưng đó là một trực giác ban đầu đủ tốt, đặc biệt là khi bạn xem xét các loại dữ liệu đại số (ví dụ: một hiệp hai loại có thể chứa bất kỳ thành viên nào trong hai loại; đó là sự kết hợp của các bộ đó) .
Doval

@Doval: Nếu tôi viết một lớp mô tả về Khách hàng, có lẽ nó sẽ đại diện cho một "tập hợp" khách hàng, vì tôi sẽ tạo ra một bộ sưu tập các trường hợp. Nhưng nói rằng Khách hàng là Loại vì nó mô tả một "tập hợp" khách hàng là một tautology; nó có vẻ rõ ràng Điều thú vị hơn là loại Khách hàng mô tả các đặc điểm của khách hàng. Sử dụng "set" để giải thích điều này có vẻ ... trừu tượng hơn thực tế. Trừ khi, có lẽ, bạn là một nhà toán học.
Robert Harvey

2

Làm thế nào những điều này được định nghĩa đúng?

Chúng được xác định đúng bởi sự hậu thuẫn toán học cứng nhắc, cung cấp những khẳng định mạnh mẽ về những gì chúng là, cách chúng hoạt động và những gì được đảm bảo.

Nhưng các lập trình viên phần lớn không cần biết điều đó. Họ cần hiểu các khái niệm.

Giá trị

Hãy bắt đầu với các giá trị, vì mọi thứ được xây dựng từ đó. Giá trị là dữ liệu được sử dụng trong điện toán. Tùy thuộc vào cách tiếp cận, chúng là các giá trị mà mọi người đều quen thuộc: 42, 3.14, "Làm thế nào bây giờ bò nâu", hồ sơ nhân sự cho Jenny xuống trong Kế toán, v.v.

Giải thích khác của các giá trị là biểu tượng . Hầu hết các lập trình viên hiểu các ký hiệu này là "giá trị" của phép liệt kê. LeftRightlà biểu tượng cho enum Handedness(bỏ qua người và cá thuận cả hai bên).

Bất kể việc thực hiện, các giá trị là những thứ khác nhau mà ngôn ngữ làm việc với để thực hiện các phép tính.

Các loại

Vấn đề với các giá trị là không phải tất cả các tính toán đều hợp pháp cho tất cả các giá trị. 42 + goatkhông thực sự có ý nghĩa.

Đây là nơi các loại đi vào chơi. Các loại là siêu dữ liệu xác định tập hợp con của các giá trị. Các Handednessenum trên là một ví dụ điển hình. Loại này nói "chỉ LeftRightcó thể được sử dụng ở đây". Điều này cho phép các chương trình xác định rất sớm rằng các hoạt động nhất định sẽ dẫn đến lỗi.

Một cách sử dụng thực tế khác để xem xét là dưới mui xe, máy tính làm việc với byte. Byte 42 có thể có nghĩa là số 42 hoặc có thể có nghĩa là ký tự * hoặc có thể có nghĩa là Jenny từ Kế toán. Các loại cũng (trong sử dụng thực tế, không phải trên lý thuyết rất nhiều) giúp xác định mã hóa cho bộ sưu tập byte cơ bản được sử dụng bởi các máy tính.

Các loại

Và đây là nơi chúng ta bắt đầu đi ra ngoài một chút. Vì vậy, khi một ngôn ngữ lập trình có một biến đề cập đến một loại, có loại gì?

Ví dụ, trong Java và C #, nó có loại Type(có loại Type, có ... và cứ tiếp tục như vậy). Đây là khái niệm đằng sau các loại . Trong một số ngôn ngữ, bạn có thể thực hiện một số điều hữu ích hơn với biến Loại so với Java và C #. Một khi điều đó xảy ra nó trở nên hữu ích để nói "Tôi muốn có một giá trị mà là một loại, mà còn là một số loại của IEnumerable<int>". Ta-da! Các loại.

Hầu hết các lập trình viên có thể nghĩ về các loại như các ràng buộc chung Java và C #. Hãy xem xét public class Foo<T> where T: IComparable{}. Trong một ngôn ngữ với các loại, T: kindOf(IComparable)khai báo biến trở thành hợp pháp; không chỉ là một điều đặc biệt bạn có thể làm trong khai báo lớp và hàm.

Kiểu xây dựng

Có lẽ không có gì đáng ngạc nhiên, các hàm tạo kiểu chỉ đơn giản là các hàm tạo cho các kiểu . "Nhưng làm thế nào để bạn xây dựng một loại? Các loại chỉ .". Ơ ... không nhiều lắm.

Cũng không có gì đáng ngạc nhiên, thật khó để xây dựng tất cả các tập hợp con có giá trị khác nhau mà bất kỳ chương trình máy tính nào sẽ sử dụng. Kiểu constructor hoạt động để giúp cho các lập trình viên "xây dựng" các tập con đó theo những cách có ý nghĩa.

Ví dụ phổ biến nhất của hàm tạo kiểu là một định nghĩa mảng : int[4]. Ở đây bạn chỉ định 4cho hàm tạo kiểu, sử dụng giá trị để tạo cho bạn một mảng gồm int4 mục. Nếu bạn đã chỉ định một loại đầu vào khác, bạn sẽ nhận được một loại đầu ra khác.

Generics là một dạng khác của hàm tạo kiểu, lấy kiểu khác làm đầu vào của chúng.

Trong nhiều ngôn ngữ, có một hàm tạo kiểu muốn P -> Rxây dựng một kiểu biểu thị một hàm lấy kiểu Pvà trả về kiểu R.

Bây giờ, bối cảnh sẽ xác định xem "hàm trả về một kiểu" có phải là hàm tạo kiểu hay không. Theo kinh nghiệm (giới hạn được thừa nhận) của tôi, dòng này là "bạn có thể sử dụng loại này vào thời gian biên dịch không?". Đúng? Kiểu xây dựng. Không? Chỉ là một chức năng.

Loại tham số

Vì vậy, bạn nhớ các tham số được truyền cho Type Con constructor? Chúng thường được gọi là Tham số loại, vì hình thức phổ biến của Trình xây dựng kiểu là Type[param]hoặc Type<param>.


1
Bạn có thể làm rõ / mở rộng phần về 'Loại' không? Trong Haskell, một loại có loại *, trong khi một hàm tạo kiểu (với một đối số) có loại * -> *. Các ràng buộc như (Num a) => a(có nghĩa là "bất kỳ loại anào là một thể hiện của kiểu Numchữ") không phải là các loại. Kiểu chữ Numkhông phải là một loại 'bản thân', nhưng có loại * -> Constraint. Tôi cảm thấy khó khăn khi liên hệ ý tưởng Haskell về một loại '(mà tôi cho là có liên quan chặt chẽ với các loại trong lý thuyết loại?) Với các ví dụ bạn đưa ra.
John Bartholomew

Tôi nên nói, :kindlệnh của ghci cho loại Numnhư * -> Constraint. Điều đó có thể cụ thể đối với GHC, tôi không biết.
John Bartholomew

@JohnBartholomew - Các loại Haskell có nhiều "chữ ký cho các nhà xây dựng kiểu". Thật không may, Haskell của tôi gần như không đến mức tôi có thể thoải mái nói quá nhiều về các chi tiết.
Telastyn
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.