2013-04-05 3 views
5

я пытаюсь написать отчет, который представляет банковский счет:Erlang записи с обоих типов и стоимостных ограничений, а также значения по умолчанию

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

Я также хочу, чтобы ограничить баланс, чтобы всегда быть >= 0. Как мне это сделать?

+7

Обратите внимание, что, хотя вы можете объявить тип неотрицательным целым, это ** не ** обеспечивает что-либо во время выполнения, это действительно только комментарий. Вы можете установить поле баланса для любого выбранного вами значения и любого типа. – rvirding

+0

@rviding Как я могу обеспечить выполнение этого поведения во время выполнения (без функции защиты)? Или эта псевдостатическая типизация просто не существует в Erlang? – 2rs2ts

+1

Вы не можете! Erlang слишком динамичен, поэтому в языке не существует статической типизации, и компилятор не использует эту информацию. Он предназначен только для документации и для диализатора для проверки типов. – rvirding

ответ

6

Что-то вроде balance = 0 :: 0 | pos_integer() может сделать трюк.

редактировать не был уверен, что она существовала, но non_neg_integer() бы лучше:

balance = 0 :: non_neg_integer() 
+0

Я тоже не знал, что существует. Благодаря тонну! – 2rs2ts

+2

Остерегайтесь, что, как сказал Роберт Вирддинг, это только информация для читателя, документация или инструменты. Это не препятствует тому, чтобы баланс был отрицательным или чем-либо еще (например, атомом). Если это важно в вашем коде, вы должны использовать Guard, например is_integer (B), а также B> = 0 перед его использованием и/или изменение баланса. – Pascal

+0

Спасибо @Pascal, я обязательно буду использовать эти охранники. :) – 2rs2ts

6

Как было отмечено другими, спецификации типа являются лишь входы для инструментов анализа, как 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).

+0

Спасибо за пример и предупреждение! – 2rs2ts

 Смежные вопросы

  • Нет связанных вопросов^_^