Liệu có ý nghĩa gì khi có cả khái niệm 'null' và 'Có thể'?


11

Trong khi tạo ứng dụng khách cho API web bằng C #, tôi đã gặp phải một vấn đề liên quan đến nullgiá trị mà nó sẽ đại diện cho hai điều khác nhau:

  • không có gì , ví dụ foocó thể có hoặc không cóbar
  • không xác định : theo mặc định, phản hồi API chỉ bao gồm một tập hợp các thuộc tính, bạn phải cho biết thuộc tính bổ sung nào bạn muốn. Vì vậy, không biết có nghĩa là tài sản không được yêu cầu từ API.

Sau một số tìm kiếm, tôi đã tìm ra loại Có thể (hoặc Tùy chọn), cách nó được sử dụng trong các ngôn ngữ chức năng và cách nó "giải quyết" các vấn đề hội thảo null bằng cách buộc người dùng nghĩ về sự vắng mặt của giá trị. Tuy nhiên, tất cả các tài nguyên tôi gặp đều nói về việc thay thế null bằng Có thể . Tôi đã tìm thấy một số đề cập về logic ba giá trị , nhưng tôi không hiểu đầy đủ về nó và hầu hết các lần đề cập đến của nó là trong bối cảnh "đó là một điều xấu".

Bây giờ tôi đang tự hỏi liệu có ý nghĩa gì khi có cả khái niệm nullCó thể , đại diện cho ẩn sốkhông có gì tương ứng. Đây có phải là logic ba giá trị mà tôi đọc được không, hay nó có tên khác không? Hoặc là cách dự định để làm tổ Có thể trong Có thể?


9
Nó không làm cho sanse có null. Đó là một ý tưởng hoàn toàn bị phá vỡ.
Andrej Bauer

7
Đừng tạo ra một cái có thể có thể có ngữ nghĩa khác với một cái có thể. Có lẽ là một đơn nguyên , và một trong những luật đơn nguyên là M M xM xnên có cùng một ngữ nghĩa.
Eric Lippert

2
Bạn có thể xem xét việc thiết kế các phiên bản đầu của Visual Basic, không có gì (tham chiếu đến không có đối tượng), Null (cơ sở dữ liệu null), Empty (một biến không được khởi tạo) và Thiếu (một tham số tùy chọn không được thông qua). Thiết kế này rất phức tạp và theo nhiều cách không nhất quán, nhưng có một lý do tại sao những khái niệm đó không được kết hợp với nhau.
Eric Lippert

3
Có, tôi sẽ không sử dụng Có lẽ cả. Nhưng, trên thực tế, cũng giống như Maybe alà giống như , ngữ nghĩa, cũng giống như một + 1 + 1 , và đẳng cấu với gõ từ @Andej câu trả lời. Bạn cũng có thể xác định thể hiện đơn nguyên của riêng mình cho loại này và do đó sử dụng các tổ hợp đơn nguyên khác nhau. a+1Maybe Maybe aa+1+1UserInput a
Euge

12
@EricLippert: sai là " M (M x)M xnên có cùng ngữ nghĩa". Lấy M = Liství dụ: danh sách các danh sách không giống với danh sách. Khi Mlà một đơn nguyên, có một chuyển đổi (cụ thể là nhân đơn nguyên) từ M (M x)để M xgiải thích mối quan hệ giữa họ, nhưng họ không có "cùng một ngữ nghĩa".
Andrej Bauer

Câu trả lời:


14

Các nullgiá trị như một món quà mặc định ở khắp mọi nơi chỉ là một ý tưởng thực sự bị phá vỡ , vì vậy quên về điều đó.

Bạn phải luôn có chính xác khái niệm mô tả đúng nhất dữ liệu thực tế của bạn. Nếu bạn cần một loại chỉ ra "không rõ ràng", "không có gì" và "giá trị", bạn nên có chính xác điều đó. Nhưng nếu nó không phù hợp với nhu cầu thực tế của bạn thì bạn không nên làm điều đó. Đó là một ý tưởng tốt để xem những gì người khác sử dụng và những gì họ đã đề xuất, nhưng bạn không phải theo dõi họ một cách mù quáng.

Những người thiết kế thư viện tiêu chuẩn cố gắng đoán các mô hình sử dụng phổ biến và họ thường làm tốt điều đó, nhưng nếu có một điều cụ thể bạn cần, bạn nên tự xác định nó. Chẳng hạn, trong Haskell, bạn có thể xác định:

data UnknownNothing a =
     Unknown
   | Nothing
   | Value a

Nó thậm chí có thể là trường hợp bạn nên sử dụng một cái gì đó mô tả dễ nhớ hơn:

data UserInput a =
     Unknown
   | NotGiven
   | Answer a

Bạn có thể định nghĩa 10 loại như vậy sẽ được sử dụng cho các tình huống khác nhau. Hạn chế là bạn sẽ không có sẵn các chức năng thư viện có sẵn (chẳng hạn như các chức năng của thư viện Maybe), nhưng điều này thường trở thành một chi tiết. Không khó để thêm chức năng của riêng bạn.


Làm cho cảm giác hoàn hảo khi bạn thấy nó đánh vần như vậy. Tôi chủ yếu quan tâm đến ý tưởng, hỗ trợ thư viện hiện tại chỉ là một phần thưởng :)
Stijn

Nếu chỉ có rào cản để tạo ra các loại như vậy là thấp hơn trong nhiều ngôn ngữ. : /
Raphael

Nếu chỉ có người ngừng sử dụng ngôn ngữ từ những năm 1960 (hoặc tái sinh của họ từ những năm 1980).
Andrej Bauer

@AndrejBauer: cái gì? nhưng sau đó các nhà lý thuyết sẽ không có gì để phàn nàn!
Yttrill

Chúng tôi không phàn nàn, chúng tôi đang ở trên cao.
Andrej Bauer

4

Theo như tôi biết, giá trị nulltrong C # là giá trị có thể cho một số biến, tùy thuộc vào loại của nó (tôi có đúng không?). Ví dụ, trường hợp của một số lớp. Đối với phần còn lại của các loại (như int, boolv.v.), bạn có thể thêm giá trị ngoại lệ này bằng cách khai báo các biến bằng int?hoặc bool?thay vào đó (đây chính xác là những gì hàm Maybetạo thực hiện, như tôi sẽ mô tả tiếp theo).

Hàm tạo Maybekiểu từ lập trình hàm thêm giá trị ngoại lệ mới này cho kiểu dữ liệu đã cho. Vì vậy, nếu Intlà loại số nguyên hoặc Gamelà loại trạng thái của trò chơi, thì Maybe Intcó tất cả các số nguyên cộng với giá trị null (đôi khi được gọi là không có gì ). Tương tự cho Maybe Game. Ở đây, không có loại đi kèm với nullgiá trị. Bạn thêm nó khi bạn cần nó.

IMO, phương pháp cuối cùng này là phương pháp tốt hơn cho bất kỳ ngôn ngữ lập trình nào.


Vâng, sự hiểu biết của bạn về C # là chính xác. Vì vậy, tôi có thể suy luận từ câu trả lời của bạn rằng bạn đề nghị lồng Maybevà thả nullhoàn toàn?
Stijn

2
Ý kiến ​​của tôi là về cách một ngôn ngữ nên được. Tôi không thực sự biết các thực hành tốt nhất về lập trình C #. Trong trường hợp của bạn, tùy chọn tốt nhất sẽ là xác định kiểu dữ liệu như @Andrej Bauer đã mô tả, nhưng tôi nghĩ bạn không thể làm điều đó trong C #.
Euge

@Euge: Các loại tổng đại số có thể (xấp xỉ) xấp xỉ với phân nhóm tin nhắn đa hình kiểu OO cổ điển và gửi tin nhắn đa hình kiểu phụ. Đó là cách nó được thực hiện trong Scala, ví dụ, với một vòng xoắn bổ sung để cho phép các kiểu đóng . Kiểu này trở thành một siêu lớp trừu tượng, các hàm tạo kiểu trở thành các lớp con cụ thể, khớp mẫu trên các hàm tạo có thể được xấp xỉ bằng cách di chuyển các trường hợp thành các phương thức quá tải trong các lớp con hoặc isinstancekiểm tra cụ thể . Scala cũng có kết hợp mẫu "phù hợp", và C thực sự cũng vừa mới đạt được các mẫu đơn giản.
Jörg W Mittag

"Vòng xoắn bổ sung" mà tôi đã đề cập cho Scala là ngoài "công cụ sửa đổi" tiêu chuẩn "ảo" (đối với một lớp có thể được kế thừa từ) và final(đối với một lớp không thể được kế thừa từ), nó cũng có sealed, cho một lớp chỉ có thể được mở rộng trong cùng một đơn vị biên dịch . Điều này có nghĩa là trình biên dịch có thể biết tĩnh tất cả các lớp con có thể (miễn là tất cả chúng đều là chính chúng sealedhoặc final) và do đó kiểm tra mức độ phù hợp cho các mẫu khớp, điều này không thể thực hiện được đối với một ngôn ngữ có tải mã thời gian chạy và kế thừa không bị hạn chế.
Jörg W Mittag

Tất nhiên bạn có thể thiết kế các loại với các ngữ nghĩa đó trong C #. Lưu ý rằng C # không cho phép loại có thể tích hợp (được gọi là Nullable trong C #) được lồng vào nhau. int??là bất hợp pháp trong C #.
Eric Lippert

3

Nếu bạn có thể định nghĩa các hiệp hội loại, như X or Yvà nếu bạn có một loại được đặt tên Nullchỉ đại diện cho nullgiá trị (và không có gì khác), thì đối với bất kỳ loại nào T, T or Nullthực sự không quá khác biệt Maybe T. Vấn đề duy nhất nulllà khi ngôn ngữ coi một loại TT or Nullngầm, tạo ra nullmột giá trị hợp lệ cho mọi loại (ngoại trừ các loại nguyên thủy trong các ngôn ngữ có chúng). Đây là những gì xảy ra trong ví dụ trong Java, nơi một hàm Stringcũng chấp nhận null.

Nếu bạn coi các loại là tập hợp các giá trị và orlà tập hợp của các tập hợp, thì (T or Null) or Nullđại diện cho tập hợp các giá trị T ∪ {null} ∪ {null}, giống như T or Null. Có các trình biên dịch thực hiện loại phân tích này (SBCL). Như đã nói trong các nhận xét, bạn không thể dễ dàng phân biệt giữa Maybe TMaybe Maybe Tkhi bạn xem các loại dưới dạng tên miền (mặt khác, chế độ xem đó khá hữu ích cho các chương trình được nhập động). Nhưng bạn cũng có thể giữ các loại dưới dạng biểu thức đại số, trong đó (T or Null) or Nullđại diện cho nhiều cấp độ của Maybes.


2
Trên thực tế, điều quan trọng Maybe (Maybe X)là nó không giống như Maybe X. Nothingkhông phải là điều tương tự như Just Nothing. Kết Maybe (Maybe X)hợp với Maybe Xlàm cho nó không thể điều trị Maybe _đa hình.
Gilles 'SO- ngừng trở nên xấu xa'

1

Bạn cần tìm ra những gì bạn muốn thể hiện, và sau đó tìm cách để thể hiện nó.

Đầu tiên, bạn có các giá trị trong miền vấn đề của bạn (như số, chuỗi, hồ sơ khách hàng, booleans, v.v.). Thứ hai, bạn có một số giá trị chung, bổ sung trên đầu các giá trị miền vấn đề của bạn: Ví dụ: "nothing" (sự vắng mặt đã biết của một giá trị), "không xác định" (không có kiến ​​thức về sự hiện diện hay vắng mặt của một giá trị), không được đề cập là " một cái gì đó "(chắc chắn có một giá trị, nhưng chúng ta không biết cái nào). Có lẽ bạn có thể nghĩ về các tình huống khác.

Nếu bạn muốn có thể biểu diễn tất cả các giá trị cộng với ba giá trị bổ sung này, bạn sẽ tạo một enum với các trường hợp "không có gì", "không xác định", "một cái gì đó" và "giá trị" và đi từ đó.

Trong một số ngôn ngữ, một số trường hợp đơn giản hơn. Ví dụ: trong Swift, bạn có cho mọi loại T loại "T tùy chọn" có giá trị có thể bằng 0 cộng với tất cả các giá trị của T. Điều này cho phép bạn xử lý nhiều tình huống một cách dễ dàng và hoàn hảo nếu bạn không có "gì" và " giá trị". Bạn có thể sử dụng nó nếu bạn có "giá trị" và "giá trị". Bạn không thể sử dụng nó nếu bạn cần xử lý "không có gì", "không xác định" và "giá trị". Các ngôn ngữ khác có thể sử dụng "null" hoặc "có thể", nhưng đó chỉ là một từ khác.

Trong JSON, bạn có từ điển với các cặp khóa / giá trị. Ở đây, một khóa có thể bị thiếu trong từ điển hoặc nó có thể có giá trị null. Vì vậy, bằng cách mô tả cẩn thận cách mọi thứ được lưu trữ, bạn có thể biểu thị các giá trị chung cộng với hai giá trị bổ sung.

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.