代码之家  ›  专栏  ›  技术社区  ›  2rs2ts

具有类型和值限制以及默认值的Erlang记录

  •  5
  • 2rs2ts  · 技术社区  · 13 年前

    我正试图写一份代表银行账户的记录:

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

    我还想限制余额始终为 >= 0 。我该怎么做?

    2 回复  |  直到 13 年前
        1
  •  7
  •   Martin Törnwall    10 年前

    正如其他人所指出的,类型规范只是分析工具的输入,如 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 ).

        2
  •  6
  •   lmsteffan    13 年前

    有点像 balance = 0 :: 0 | pos_integer() 可能会奏效。

    编辑不确定它是否存在,但是 non_neg_integer() 会更好:

    balance = 0 :: non_neg_integer()