2013-04-05 26 views
5

Tôi cố gắng để viết một kỷ lục mà đại diện cho một tài khoản ngân hàng:hồ sơ Erlang với cả hai loại và hạn chế giá trị cũng như các giá trị mặc định

-record(account, { name :: atom(), 
        type :: atom(), 
        balance = 0 :: integer() }). 

Tôi cũng muốn hạn chế sự cân bằng để luôn >= 0. Làm thế nào để tôi làm điều này?

+7

Lưu ý rằng mặc dù bạn có thể khai báo loại là số nguyên không âm này ** không ** thực thi bất cứ điều gì trong thời gian chạy, nó thực sự chỉ là một chú thích. Bạn có thể đặt trường số dư thành bất kỳ giá trị nào bạn chọn và bất kỳ loại nào. – rvirding

+0

@rviding Làm cách nào để thực thi hành vi này khi chạy (không có bộ phận bảo vệ chức năng)? Hay việc gõ giả tĩnh này không tồn tại trong Erlang? – 2rs2ts

+1

Bạn không thể! Erlang là quá năng động, do đó, gõ tĩnh không tồn tại trong ngôn ngữ và trình biên dịch không sử dụng thông tin này. Nó chỉ dành cho tài liệu hướng dẫn và cho công cụ kiểm tra kiểu dialyzer. – rvirding

Trả lời

6

Một cái gì đó như balance = 0 :: 0 | pos_integer() có thể thực hiện thủ thuật.

chỉnh sửa là không chắc chắn nó tồn tại, nhưng non_neg_integer() sẽ tốt hơn:

balance = 0 :: non_neg_integer() 
+0

Tôi cũng không biết là đã tồn tại. Cảm ơn một tấn! – 2rs2ts

+2

Hãy coi chừng rằng như đã nói Robert Virding đây chỉ là thông tin cho người đọc, tài liệu hoặc công cụ. Nó không ngăn cản sự cân bằng là tiêu cực hay bất cứ điều gì khác (giống như một nguyên tử). Nếu nó quan trọng trong mã của bạn, bạn phải sử dụng bảo vệ như is_integer (B) và B = 0 trước khi sử dụng nó và/hoặc sửa đổi số dư. – Pascal

+0

Cảm ơn @Pascal, tôi chắc chắn sẽ sử dụng những vệ sĩ đó. :) – 2rs2ts

6

Theo ghi nhận của người khác, các thông số kỹ thuật loại chỉ đơn thuần là đầu vào cho công cụ phân tích như PropErDialyzer. Nếu bạn cần để thực thi bất biến balance >= 0, các loại tài khoản nên được đóng gói, chỉ có thể truy cập đến chức năng mà tôn trọng bất biến:

-module(account). 

-record(account, { name :: atom(), 
        type :: atom(), 
        balance = 0 :: non_neg_integer() }). 

%% Declares a type whose structure should not be visible externally. 
-opaque account() :: #account{}. 
%% Exports the type, making it available to other modules as 'account:account()'. 
-export_type([account/0]). 

%% Account constructor. Used by other modules to create accounts. 
-spec new(atom(), atom(), non_neg_integer()) -> account(). 
new(Name, Type, InitialBalance) -> 
    A = #account{name=Name, type=Type}, 
    set_balance(A, InitialBalance). 

%% Safe setter - checks the balance invariant 
-spec set_balance(account(), non_neg_integer()) -> account(). 
set_balance(Account, Balance) when is_integer(Balance) andalso Balance >= 0 -> 
    Account#account{balance=Balance}; 
set_balance(_, _) -> error(badarg). % Bad balance 

Chú ý cách này giống như một lớp học với lĩnh vực tư nhân trong các ngôn ngữ hướng đối tượng như Java hay C++ . Bằng cách hạn chế quyền truy cập vào các hàm tạo và các trình truy cập "đáng tin cậy", sự bất biến được thực thi.

Giải pháp này không cung cấp bảo vệ chống lại độc hại sửa đổi trường balance. Hoàn toàn có thể cho mã trong mô-đun khác bỏ qua đặc tả loại "đục" và thay thế trường số dư trong bản ghi (kể từ records are just tuples).

+0

Cảm ơn ví dụ và cảnh báo! – 2rs2ts

Các vấn đề liên quan