Tại sao (hoặc tại sao không) là các loại tồn tại được coi là thực tiễn xấu trong lập trình chức năng?


43

Một số kỹ thuật tôi có thể sử dụng để mã hóa lại một cách nhất quán loại bỏ sự phụ thuộc vào các kiểu tồn tại là gì? Thông thường, chúng được sử dụng để loại bỏ các cấu trúc không mong muốn của loại của bạn cũng như cho phép tiêu thụ với tối thiểu kiến ​​thức về loại đã cho (hoặc theo cách hiểu của tôi).

Có ai đã đưa ra một cách nhất quán đơn giản để loại bỏ sự phụ thuộc vào những điều này trong mã mà vẫn duy trì một số lợi ích? Hoặc ít nhất là bất kỳ cách nào để trượt trong một bản tóm tắt cho phép loại bỏ chúng mà không yêu cầu mã số đáng kể để đối phó với sự thay đổi?

Bạn có thể đọc thêm về các loại tồn tại ở đây ("nếu bạn dám ..").


8
@RobertHarvey Tôi đã tìm thấy bài đăng trên blog khiến tôi suy nghĩ về câu hỏi này lần đầu tiên: Haskell Antipotype: Execential typeclass . Ngoài ra, bài báo Joey Adams đề cập đến mô tả một số poblem có tồn tại trong Phần 3.1. Nếu bạn có lập luận trái ngược, xin vui lòng chia sẻ chúng.
Petr Pudlák

5
@ PetrPudlák: Hãy nhớ rằng antipotype nói chung không có loại tồn tại, nhưng sử dụng cụ thể khi một cái gì đó đơn giản và dễ dàng hơn (và được hỗ trợ tốt hơn trong Haskell) sẽ làm công việc tương tự.
CA McCann

10
Nếu bạn muốn biết lý do tại sao tác giả của một bài đăng trên blog cụ thể bày tỏ ý kiến ​​thì người bạn có lẽ nên hỏi là tác giả.
Eric Lippert

6
@Ptolemy Đó là vấn đề quan điểm. Sử dụng Haskell trong nhiều năm, tôi khó có thể tưởng tượng việc sử dụng một ngôn ngữ chức năng không có hệ thống loại mạnh.
Petr Pudlák

4
@Ptolemy: Câu thần chú của Haskell là "nếu nó biên dịch nó hoạt động", câu thần chú của Erlang là "hãy để nó sụp đổ". Gõ động là tốt như một chất keo, nhưng cá nhân tôi sẽ không xây dựng mọi thứ chỉ bằng keo.
Den

Câu trả lời:


8

Các kiểu hiện sinh không thực sự được coi là thực hành xấu trong lập trình chức năng. Tôi nghĩ điều khiến bạn vấp ngã là một trong những cách sử dụng phổ biến nhất cho hiện sinh là các phản mẫu kiểu chữ hiện sinh , điều mà nhiều người tin là thực hành xấu.

Mẫu này thường được tìm ra như một câu trả lời cho câu hỏi làm thế nào để có một danh sách các phần tử được gõ không đồng nhất mà tất cả đều thực hiện cùng một kiểu chữ. Ví dụ: bạn có thể muốn có một danh sách các giá trị có Showthể hiện:

{-# LANGUAGE ExistentialTypes #-}

class Shape s where
   area :: s -> Double

newtype Circle = Circle { radius :: Double }
instance Shape Circle where
   area (Circle r) = pi * r^2

newtype Square = Square { side :: Double }
    area (Square s) = s^2

data AnyShape = forall x. Shape x => AnyShape x
instance Shape AnyShape where
    area (AnyShape x) = area x

example :: [AnyShape]
example = [AnyShape (Circle 1.0), AnyShape (Square 1.0)]

Vấn đề với mã như thế này là:

  1. Hoạt động hữu ích duy nhất bạn có thể thực hiện trên một AnyShapelà lấy khu vực của nó.
  2. Bạn vẫn cần sử dụng hàm AnyShapetạo để đưa một trong các kiểu hình vào AnyShapekiểu.

Vì vậy, hóa ra, đoạn mã đó không thực sự mang lại cho bạn bất cứ thứ gì mà đoạn mã ngắn hơn này không:

class Shape s where
   area :: s -> Double

newtype Circle = Circle { radius :: Double }
instance Shape Circle where
   area (Circle r) = pi * r^2

newtype Square = Square { side :: Double }
    area (Square s) = s^2

example :: [Double]
example = [area (Circle 1.0), area (Square 1.0)]

Trong trường hợp các lớp đa phương thức, hiệu ứng tương tự thường có thể được thực hiện đơn giản hơn bằng cách sử dụng "bản ghi phương thức" mã hóa thay vì sử dụng một kiểu chữ như Shape, bạn xác định một loại bản ghi có các trường là "phương thức" của Shapeloại và bạn viết các hàm để chuyển đổi các vòng tròn và hình vuông của bạn thành Shapes.


Nhưng điều đó không có nghĩa là các loại tồn tại là một vấn đề! Ví dụ, trong Rust, chúng có một tính năng gọi là các đối tượng đặc điểm mà mọi người thường mô tả là một kiểu tồn tại trên một đặc điểm (phiên bản của các lớp loại của Rust). Nếu kiểu chữ hiện sinh là một phản mẫu trong Haskell, điều đó có nghĩa là Rust chọn một giải pháp tồi? Không! Động lực trong thế giới Haskell là về cú pháp và sự thuận tiện, không thực sự về nguyên tắc.

Một cách toán học hơn để đưa ra điều này là chỉ ra rằng AnyShapeloại từ phía trên và Doublecấu trúc đẳng cấu là một "chuyển đổi không mất mát" giữa chúng (tốt, tiết kiệm cho độ chính xác của dấu phẩy động):

forward :: AnyShape -> Double
forward = area

backward :: Double -> AnyShape
backward x = AnyShape (Square (sqrt x))

Nói một cách nghiêm túc, bạn không được hoặc mất bất kỳ quyền lực nào bằng cách chọn cái này so với cái kia. Điều đó có nghĩa là sự lựa chọn nên dựa trên các yếu tố khác như dễ sử dụng hoặc hiệu suất.


Và hãy nhớ rằng các kiểu tồn tại có những cách sử dụng khác bên ngoài ví dụ về danh sách không đồng nhất này, vì vậy thật tốt khi có chúng. Ví dụ, STloại của Haskell , cho phép chúng ta viết các hàm hoàn toàn bên ngoài nhưng sử dụng bên trong các hoạt động đột biến bộ nhớ, sử dụng một kỹ thuật dựa trên các loại tồn tại để đảm bảo an toàn tại thời gian biên dịch.

Vì vậy, câu trả lời chung là không có câu trả lời chung chung. Việc sử dụng các kiểu tồn tại chỉ có thể được đánh giá trong ngữ cảnh và các câu trả lời có thể khác nhau tùy thuộc vào các tính năng và cú pháp được cung cấp bởi các ngôn ngữ khác nhau.


Đó không phải là một sự đẳng cấu.
Ryan Reich

2

Tôi không quá quen thuộc với Haskell, vì vậy tôi sẽ cố gắng trả lời phần chung của câu hỏi với tư cách là nhà phát triển C # không học thuật.

Sau khi đọc xong, nó bật ra rằng:

  1. Ký tự đại diện Java tương tự như các kiểu tồn tại:

    Ví dụ, sự khác biệt giữa các kiểu tồn tại của Scala và ký tự đại diện của Java

  2. Ký tự đại diện không được triển khai hoàn toàn trong C #: phương sai chung được hỗ trợ, nhưng phương sai trang web gọi là không:

    Generics C #: Ký tự đại diện

  3. Bạn có thể không cần tính năng này hàng ngày, nhưng khi bạn làm bạn sẽ cảm thấy nó (ví dụ: phải giới thiệu một loại bổ sung để làm cho mọi thứ hoạt động):

    Ký tự đại diện trong các ràng buộc chung C #

Dựa trên thông tin này, các kiểu / ký tự đại diện hữu ích khi được triển khai đúng cách và không có gì sai với chúng, nhưng chúng có thể bị sử dụng sai như các tính năng ngôn ngữ khác.

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.