2011-12-14 27 views
5

thể trùng lặp:
How to create a type bounded within a certain rangegiá trị Hạn chế trong loại nhà xây dựng

Tôi có kiểu dữ liệu:

data Expr = Num Int 
      | Expression Expr Operator Expr 

Trong bối cảnh của vấn đề, những con số mà (Num Int) sẽ đại diện là chỉ một chữ số. Có cách nào để đảm bảo rằng hạn chế trong tuyên bố loại?

Tất nhiên chúng tôi có thể xác định một hàm để kiểm tra xem Expr có hợp lệ hay không, nhưng sẽ tốt hơn nếu hệ thống kiểu xử lý nó.

Trả lời

12

Bạn có thể sử dụng một kiểu dữ liệu trừu tượng với một smart constructor:

newtype Digit = Digit { digitVal :: Int } 
    deriving (Eq, Ord, Show) 

mkDigit :: Int -> Maybe Digit 
mkDigit n 
    | n >= 0 && n < 10 = Just (Digit n) 
    | otherwise = Nothing 

Nếu bạn đặt điều này trong mô-đun khác và không xuất khẩu các nhà xây dựng Digit, sau đó mã khách hàng không thể xây dựng giá trị của loại Digit nằm ngoài phạm vi [0,9], nhưng bạn phải tự quấn và mở nó ra để sử dụng nó. Bạn có thể định nghĩa một cá thể Num thực hiện phép tính số học, nếu điều đó có ích; điều đó cũng sẽ cho phép bạn sử dụng các chữ số để xây dựng các chữ số. (Tương tự như vậy cho EnumBounded.)

Tuy nhiên, điều này không đảm bảo rằng bạn không bao giờ thử để tạo ra một chữ số không hợp lệ, chỉ là bạn không bao giờ làm. Nếu bạn muốn đảm bảo hơn, thì giải pháp thủ công Jan cung cấp tốt hơn, với chi phí ít thuận tiện hơn. (Và nếu bạn định nghĩa một cá thể Num cho loại chữ số đó, nó sẽ kết thúc bằng "không an toàn", bởi vì bạn có thể viết 42 :: Digit nhờ hỗ trợ chữ số bạn muốn.)

(Nếu bạn không biết newtype là gì, đó là về cơ bản data cho dữ liệu loại với một lĩnh vực nghiêm ngặt duy nhất;. một wrapper Newtype xung quanh T sẽ có đại diện thời gian chạy giống như T đó là về cơ bản chỉ là một tối ưu hóa, vì vậy bạn có thể giả vờ nó nói data với mục đích hiểu điều này.)

Chỉnh sửa: Đối với giải pháp 100% theo định hướng lý thuyết hơn, hãy xem phần nhận xét khá chật chội của câu trả lời này.

+0

Điều đó sẽ thực hiện công việc, nhưng hệ thống kiểu không phải là điều đang xử lý sự cố. Đó là những gì tôi đang làm. –

+2

Quyền - đảm bảo bổ sung ở đây là không có giá trị nào của loại 'Chữ số' có thể không hợp lệ, trái ngược với việc chỉ xác thực một' Expr' sau sự kiện. Lưu ý rằng ngay cả giải pháp liệt kê thủ công cũng cho phép các giá trị như 'lỗi 'oops" :: Chữ số'. Có nhiều cách để xử lý những điều này một cách mạnh mẽ và thuận tiện trong các hệ thống kiểu tiên tiến, nhưng chúng chưa thực hiện theo cách của họ vào Haskell, vì vậy một sự thỏa hiệp như thế này có thể là giải pháp tốt nhất. (Không phải là họ đã làm cho nó thành bất kỳ ngôn ngữ "thế giới thực" nào khác.) – ehird

+0

Nếu hệ thống kiểu đang xử lý vấn đề, tôi sẽ không phải xác thực 'Expr' vì nó sẽ không biên dịch trong nơi đầu tiên, phải không? –

5

Vì chỉ có mười khả năng, bạn có thể sử dụng Enum để chỉ định tất cả chúng.

data Digit = Zero | One | Two deriving (Enum, Show) 

Sau đó, bạn phải sử dụng fromEnum để coi chúng là số.

1 == fromEnum One 

Tương tự như vậy, sử dụng toEnum bạn có thể có được một Digit từ một số.

toEnum 2 :: Digit 

Chúng tôi có thể tiến xa hơn và triển khai Num.

data Digit = Zero | One | Two deriving (Enum, Show, Eq) 

instance Num Digit where 
    fromInteger x = toEnum (fromInteger x) :: Digit 
    x + y = toEnum $ fromEnum x + fromEnum y 
    x * y = toEnum $ fromEnum x * fromEnum y 
    abs = id 
    signum _ = 1 

Zero + 1 + One == Two 
Các vấn đề liên quan