Nó thực sự chỉ là một hàm tạo dữ liệu bình thường được định nghĩa trong Prelude , là thư viện chuẩn được nhập tự động vào mọi mô-đun.
Có thể là gì, về mặt cấu trúc
Định nghĩa trông giống như sau:
data Maybe a = Just a
| Nothing
Khai báo đó xác định một kiểu, Maybe a
được tham số hóa bởi một biến kiểu a
, điều này có nghĩa là bạn có thể sử dụng nó với bất kỳ kiểu nào thay cho a
.
Xây dựng và Phá hủy
Kiểu có hai hàm tạo, Just a
và Nothing
. Khi một kiểu có nhiều hàm tạo, điều đó có nghĩa là một giá trị của kiểu phải được xây dựng chỉ với một trong các hàm tạo có thể. Đối với loại này, một giá trị được xây dựng thông qua Just
hoặc Nothing
, không có khả năng nào khác (không lỗi).
Vì Nothing
không có kiểu tham số, khi nó được sử dụng như một phương thức khởi tạo, nó đặt tên cho một giá trị hằng số là thành viên của kiểu Maybe a
cho tất cả các kiểu a
. Nhưng hàm Just
tạo có tham số kiểu, có nghĩa là khi được sử dụng làm hàm tạo, nó hoạt động giống như một hàm từ kiểu a
sang Maybe a
, tức là nó có kiểua -> Maybe a
Vì vậy, các hàm tạo của một kiểu xây dựng một giá trị của kiểu đó; mặt khác của vấn đề là khi nào bạn muốn sử dụng giá trị đó và đó là lúc kết hợp mẫu có tác dụng. Không giống như các hàm, các hàm tạo có thể được sử dụng trong các biểu thức ràng buộc mẫu và đây là cách mà bạn có thể thực hiện phân tích trường hợp các giá trị thuộc về các kiểu có nhiều hơn một hàm tạo.
Để sử dụng một Maybe a
giá trị trong một đối sánh mẫu, bạn cần cung cấp một mẫu cho mỗi hàm tạo, như sau:
case maybeVal of
Nothing -> "There is nothing!"
Just val -> "There is a value, and it is " ++ (show val)
Trong biểu thức trường hợp đó, mẫu đầu tiên sẽ khớp nếu giá trị là Nothing
và mẫu thứ hai sẽ khớp nếu giá trị được tạo với Just
. Nếu cái thứ hai khớp, nó cũng liên kết tên val
với tham số đã được chuyển cho hàm Just
tạo khi giá trị mà bạn đang khớp với được tạo.
Có thể có nghĩa là gì
Có thể bạn đã quen với cách thức hoạt động của nó; thực sự không có bất kỳ phép thuật nào đối với Maybe
các giá trị, nó chỉ là một Kiểu dữ liệu đại số Haskell (ADT) bình thường. Nhưng nó được sử dụng khá nhiều vì nó có hiệu quả "nâng" hoặc mở rộng một kiểu, chẳng hạn như Integer
từ ví dụ của bạn, sang một ngữ cảnh mới trong đó nó có giá trị phụ ( Nothing
) thể hiện sự thiếu giá trị! Các hệ thống kiểu sau đó yêu cầu bạn kiểm tra cho rằng giá trị tăng thêm trước khi nó sẽ cho phép bạn nhận được ở Integer
đó có thể có mặt ở đó. Điều này ngăn chặn một số lỗi đáng chú ý.
Nhiều ngôn ngữ ngày nay xử lý loại giá trị "không có giá trị" này thông qua tham chiếu NULL. Tony Hoare, một nhà khoa học máy tính lỗi lạc (ông đã phát minh ra Quicksort và là người đoạt giải Turing), sở hữu điều này là "sai lầm tỷ đô la" của mình . Loại Có thể không phải là cách duy nhất để khắc phục điều này, nhưng nó đã được chứng minh là một cách hiệu quả để làm điều đó.
Có thể là một Functor
Ý tưởng chuyển đổi kiểu này sang kiểu khác sao cho các phép toán trên kiểu cũ cũng có thể được chuyển đổi để hoạt động trên kiểu mới là khái niệm đằng sau lớp kiểu Haskell được gọi Functor
, Maybe a
có một ví dụ hữu ích.
Functor
cung cấp một phương thức được gọi là fmap
ánh xạ các hàm có phạm vi trên các giá trị từ kiểu cơ sở (chẳng hạn như Integer
) đến các hàm có phạm vi trên các giá trị từ kiểu nâng lên (chẳng hạn như Maybe Integer
). Một hàm được chuyển đổi với fmap
để hoạt động trên một Maybe
giá trị hoạt động như sau:
case maybeVal of
Nothing -> Nothing -- there is nothing, so just return Nothing
Just val -> Just (f val) -- there is a value, so apply the function to it
Vì vậy, nếu bạn có một Maybe Integer
giá trị m_x
và một Int -> Int
hàm f
, bạn có thể fmap f m_x
áp dụng hàm f
trực tiếp cho hàm Maybe Integer
mà không cần lo lắng nếu nó thực sự có giá trị hay không. Trên thực tế, bạn có thể áp dụng toàn bộ chuỗi Integer -> Integer
hàm nâng lên cho Maybe Integer
các giá trị và chỉ phải lo lắng về việc kiểm tra rõ ràng Nothing
một lần khi bạn hoàn thành.
Có thể là một Đơn nguyên
Tôi không chắc bạn đã quen với khái niệm này như thế Monad
nào, nhưng ít nhất bạn đã từng sử dụng IO a
trước đây, và kiểu chữ ký IO a
trông khá giống với Maybe a
. Mặc dù IO
đặc biệt ở chỗ nó không để lộ các hàm tạo của nó cho bạn và do đó chỉ có thể được "chạy" bởi hệ thống thời gian chạy Haskell, nó cũng vẫn là một yếu tố Functor
bổ sung Monad
. Trên thực tế, có một ý nghĩa quan trọng trong đó a Monad
chỉ là một loại đặc biệt Functor
với một số tính năng bổ sung, nhưng đây không phải là nơi để đạt được điều đó.
Dù sao, Monads thích IO
các kiểu ánh xạ thành các kiểu mới đại diện cho "các phép tính dẫn đến giá trị" và bạn có thể nâng các hàm thành Monad
các kiểu thông qua một hàm rất fmap
giống được gọi là liftM
biến một hàm thông thường thành một "tính toán dẫn đến giá trị thu được bằng cách đánh giá chức năng."
Bạn có thể đã đoán ra (nếu bạn đã đọc đến đây) Maybe
cũng là một Monad
. Nó đại diện cho "các phép tính không thể trả về giá trị". Cũng giống như với fmap
ví dụ, điều này cho phép bạn thực hiện toàn bộ các phép tính mà không cần phải kiểm tra lỗi rõ ràng sau mỗi bước. Và trên thực tế, cách Monad
cá thể được xây dựng, việc tính toán trên Maybe
các giá trị sẽ dừng ngay khi Nothing
gặp phải, vì vậy nó giống như một sự hủy bỏ ngay lập tức hoặc một sự trở lại vô giá trị ở giữa một quá trình tính toán.
Bạn có thể đã viết có thể
Như tôi đã nói trước đây, không có gì cố hữu đối với Maybe
kiểu được đưa vào cú pháp ngôn ngữ hoặc hệ thống thời gian chạy. Nếu Haskell không cung cấp nó theo mặc định, bạn có thể tự cung cấp tất cả các chức năng của nó! Trên thực tế, bạn có thể tự mình viết lại nó, với các tên khác nhau và có cùng chức năng.
Hy vọng rằng bạn đã hiểu về Maybe
kiểu và các hàm tạo của nó, nhưng nếu vẫn còn điều gì chưa rõ, hãy cho tôi biết!
Maybe
những ngôn ngữ khác sẽ sử dụngnull
hoặcnil
(với những thứ khó chịuNullPointerException
ẩn nấp trong mọi ngóc ngách). Bây giờ các ngôn ngữ khác cũng bắt đầu sử dụng cấu trúc này: Scala asOption
, và ngay cả Java 8 cũng sẽ cóOptional
kiểu này.