Tại sao các công đoàn phân biệt đối xử liên quan đến lập trình chức năng?


30

Trong nhiều năm lập trình OO, tôi đã hiểu các công đoàn bị phân biệt đối xử là gì, nhưng tôi không bao giờ thực sự bỏ lỡ chúng. Gần đây tôi đã thực hiện một số chương trình chức năng trong C # và bây giờ tôi thấy tôi cứ ước mình có chúng. Điều này đang gây trở ngại cho tôi bởi vì trên phương diện của nó, khái niệm về các công đoàn bị phân biệt đối xử có vẻ khá độc lập với sự phân đôi chức năng / OO.

Có điều gì đó vốn có trong lập trình chức năng làm cho các công đoàn bị phân biệt đối xử trở nên hữu ích hơn so với trong OO, hoặc bằng cách buộc bản thân phải phân tích vấn đề theo cách "tốt hơn", tôi chỉ đơn giản là nâng cao tiêu chuẩn của mình và bây giờ yêu cầu tốt hơn mô hình?


Vấn đề về biểu thức en.wikipedia.org/wiki/Expression_probols có thể có liên quan
xji

Nó không thực sự là một câu trả lời thích hợp cho câu hỏi, vì vậy tôi sẽ trả lời dưới dạng nhận xét, nhưng ngôn ngữ lập trình Ceylon có các loại kết hợp và chúng dường như len lỏi vào các ngôn ngữ mô hình hỗn hợp OO / hỗn hợp khác - TypeScript và Scala xuất hiện trong tâm trí tôi. Ngoài ra enum ngôn ngữ Java có thể được sử dụng như một loại thực hiện của các hiệp hội bị phân biệt đối xử.
Roland Tepp

Câu trả lời:


45

Công đoàn phân biệt đối xử thực sự tỏa sáng kết hợp với khớp mẫu, trong đó bạn chọn hành vi khác nhau tùy thuộc vào các trường hợp. Nhưng mô hình này về cơ bảnphản đối với các nguyên tắc OO thuần túy.

Trong OO thuần túy, sự khác biệt trong hành vi nên được xác định bởi chính các loại (đối tượng) và được gói gọn. Vì vậy, sự tương đương với khớp mẫu sẽ là gọi một phương thức duy nhất trên chính đối tượng, sau đó bị quá tải bởi các kiểu con trong câu hỏi để xác định hành vi khác nhau. Kiểm tra loại đối tượng từ bên ngoài (đó là những gì khớp mẫu) được coi là một phản mẫu.

Sự khác biệt cơ bản là dữ liệu và hành vi là riêng biệt trong lập trình chức năng, trong khi dữ liệu và hành vi được gói gọn trong OO.

Đây là lý do lịch sử. Một ngôn ngữ như C # đang phát triển từ ngôn ngữ OO cổ điển sang ngôn ngữ đa mô hình bằng cách kết hợp ngày càng nhiều tính năng chức năng.


6
Loại kết hợp / tổng phân biệt đối xử không giống như cây thừa kế hoặc nhiều lớp thực hiện giao diện; đó là một loại với nhiều loại giá trị. Họ cũng không ngăn chặn đóng gói; khách hàng không cần biết bạn có nhiều loại giá trị. Xem xét một danh sách được liên kết, có thể là một nút trống, hoặc một giá trị và tham chiếu đến một nút khác. Nếu không có kiểu tổng, cuối cùng bạn sẽ hack một liên kết phân biệt đối xử bằng cách có các biến cho giá trị và tham chiếu, nhưng coi một đối tượng có tham chiếu null là nút trống và giả vờ biến giá trị không tồn tại.
Doval

2
Nói chung, bạn kết thúc bao gồm các biến cho tất cả các loại giá trị có thể có trong một lớp, cộng với một loại cờ hoặc enum để cho bạn biết loại giá trị đó là gì và nhảy xung quanh các biến không tương ứng với điều đó loại.
Doval

7
@Doval Có mã hóa "tiêu chuẩn" của loại dữ liệu đại số thành các lớp bằng cách có một lớp cơ sở giao diện / trừu tượng đại diện cho loại tổng thể và bằng cách có một lớp con cho từng trường hợp của loại. Để xử lý khớp mẫu, bạn có một phương thức lấy một hàm cho từng trường hợp, vì vậy đối với một danh sách bạn có trên giao diện cấp cao nhất, List<A>phương thức đó B Match<B>(B nil, Func<A,List<A>,B> cons). Ví dụ, đây chính xác là mẫu mà Smalltalk sử dụng cho booleans. Đây cũng là cách cơ bản Scala xử lý nó. Nhiều lớp được sử dụng là một chi tiết triển khai không cần phải phơi bày.
Derek Elkins

3
@Doval: Việc kiểm tra hoàn toàn các tham chiếu null cũng không thực sự được coi là OO thành ngữ.
JacquesB

@JacquesB Kiểm tra null là một chi tiết thực hiện. Đối với máy khách của danh sách được liên kết, thường có một isEmptyphương thức kiểm tra xem tham chiếu đến nút tiếp theo có phải là null không.
Doval

35

Đã lập trình trong Pascal và Ada trước khi học lập trình chức năng, tôi không liên kết các hiệp hội phân biệt đối xử với lập trình chức năng.

Các công đoàn bị phân biệt đối xử theo một cách nào đó là kép của thừa kế. Đầu tiên cho phép dễ dàng thêm các hoạt động trên một tập hợp các loại cố định (những loại trong liên kết) và kế thừa cho phép dễ dàng thêm các loại với một bộ hoạt động cố định. (Cách dễ dàng thêm cả hai được gọi là vấn đề biểu thức ; đó là một vấn đề đặc biệt khó khăn đối với các ngôn ngữ có hệ thống kiểu tĩnh.)

Do sự nhấn mạnh của OO đối với các loại và sự nhấn mạnh kép của lập trình chức năng đối với các chức năng, các ngôn ngữ lập trình chức năng có ái lực tự nhiên đối với các loại kết hợp và cung cấp các cấu trúc cú pháp để dễ sử dụng.


7

Các kỹ thuật lập trình bắt buộc, như thường được sử dụng trong OO, thường dựa vào hai mẫu:

  1. Thành công hoặc ném ngoại lệ,
  2. Quay trở lại nullcho biết "không có giá trị" hoặc thất bại.

Mô hình chức năng thường tránh cả hai điều này, thích trả về loại hỗn hợp biểu thị lý do thành công / thất bại hoặc giá trị / không có giá trị.

Công đoàn phân biệt đối xử phù hợp với hóa đơn cho các loại hợp chất. Ví dụ, trong trường hợp đầu tiên, bạn có thể trả về truehoặc một số cấu trúc dữ liệu mô tả lỗi. Trong trường hợp thứ hai, một liên kết có chứa một giá trị, hoặc none, nilv.v ... Trường hợp thứ hai rất phổ biến, nhiều ngôn ngữ chức năng có loại "có thể" hoặc "tùy chọn" tích hợp để thể hiện giá trị đó / không liên kết.

Khi chuyển sang kiểu chức năng với ví dụ: C #, bạn sẽ nhanh chóng tìm thấy nhu cầu về các loại hợp chất này. void/thrownullkhông cảm thấy đúng với mã như vậy. Và các công đoàn phân biệt đối xử (DUs) phù hợp với dự luật. Vì vậy, bạn thấy mình muốn họ, giống như nhiều người trong chúng ta có.

Tin vui là có rất nhiều thư viện ngoài kia mô hình DU trong ví dụ C # (ví dụ, hãy xem thư viện Succinc <T> của riêng tôi ).


2

Các loại tổng thường sẽ ít hữu ích hơn trong các ngôn ngữ OO chính vì chúng đang giải quyết một loại vấn đề tương tự như phân nhóm OO. Một cách để xem xét chúng là cả hai đều xử lý phân nhóm nhưng OO opentức là người ta có thể thêm các kiểu con tùy ý vào kiểu cha và kiểu tổng là closednghĩa là người ta xác định trả trước loại con nào là hợp lệ.

Bây giờ, nhiều ngôn ngữ OO kết hợp phân nhóm với các khái niệm khác như cấu trúc kế thừa, đa hình, gõ tham chiếu, vv để làm cho chúng thường hữu ích hơn. Một hậu quả là chúng có xu hướng thiết lập nhiều công việc hơn (với các lớp và hàm tạo và không có gì) nên có xu hướng không được sử dụng cho những thứ như Results và Options cho đến khi việc gõ chung trở nên phổ biến.

Tôi cũng nói rằng việc tập trung vào các mối quan hệ trong thế giới thực mà hầu hết mọi người đã học khi họ bắt đầu lập trình OO, ví dụ Dog isa Animal, có nghĩa là Integer isa result hoặc Error isa result có vẻ hơi xa lạ. Mặc dù các ý tưởng khá giống nhau.

Về lý do tại sao các ngôn ngữ chức năng có thể thích gõ đóng hơn gõ mở, một lý do có thể là chúng có xu hướng thích khớp mẫu. Điều này rất hữu ích cho tính đa hình của hàm nhưng nó cũng hoạt động thực sự tốt với các kiểu đóng vì trình biên dịch có thể kiểm tra tĩnh xem có khớp với tất cả các kiểu con hay không. Điều này có thể làm cho ngôn ngữ cảm thấy nhất quán hơn mặc dù tôi không tin rằng có bất kỳ lợi ích vốn có nào (tôi có thể bị nhầm lẫn).


BTW: Scala đã đóng thừa kế. sealedcó nghĩa là "chỉ có thể được mở rộng trong cùng một đơn vị biên dịch", cho phép bạn đóng tập hợp các lớp con tại thời điểm thiết kế.
Jörg W Mittag

-3

Swift vui vẻ sử dụng các công đoàn phân biệt đối xử, ngoại trừ việc họ gọi họ là "enums". Enums là một trong năm loại đối tượng cơ bản trong Swift, đó là class, struct, enum, tuple và đóng. Tùy chọn Swift là các enum là các hiệp hội phân biệt đối xử và chúng thực sự cần thiết cho bất kỳ mã Swift nào.

Vì vậy, tiền đề rằng "các hiệp hội phân biệt đối xử có liên quan đến lập trình chức năng" là sai.

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.