Как было отмечено другими, спецификации типа являются лишь входы для инструментов анализа, как PropEr и Dialyzer. Если вам необходимо обеспечить соблюдение инвариантных balance >= 0
, тип счета должен быть воплощен, доступно только для функций, которые уважают инвариант:
-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
Обратите внимание, как это напоминает класс с частными полями в объектно-ориентированных языках, как Java или C++ , Ограничивая доступ к «доверенным» конструкторам и аксессуарам, применяется инвариант.
Данное решение не обеспечивает защиту от злонамеренного модификации поля balance
. Вполне возможно, что код в другом модуле игнорирует спецификацию «непрозрачного» типа и заменяет поле баланса в записи (с records are just tuples).
Обратите внимание, что, хотя вы можете объявить тип неотрицательным целым, это ** не ** обеспечивает что-либо во время выполнения, это действительно только комментарий. Вы можете установить поле баланса для любого выбранного вами значения и любого типа. – rvirding
@rviding Как я могу обеспечить выполнение этого поведения во время выполнения (без функции защиты)? Или эта псевдостатическая типизация просто не существует в Erlang? – 2rs2ts
Вы не можете! Erlang слишком динамичен, поэтому в языке не существует статической типизации, и компилятор не использует эту информацию. Он предназначен только для документации и для диализатора для проверки типов. – rvirding