Is there a way to do lookups on default record values?

左心房为你撑大大i 提交于 2019-12-12 10:13:17

问题


Given a record

-record(something, {id                :: integer(),
                    name              :: string(),
                    email = undefined :: string() | undefined}).

Is there a way to get the default values for the fields, in this example getting the fact that #something.email defaults to undefined?


回答1:


Records are syntactic sugar in Erlang, expanded by the compiler. The following solution, suggested by @Dmitry, works, but the compiler will not optimize it away unless you pass +inline, since the trick here is to really create a record:

g() -> (#something{})#something.email.

Such record syntactic sugar will be expanded to: (use erlc -E)

g() ->
    case {something,undefined,undefined,undefined} of
        {something,_,_,rec0} ->
            rec0;
        _ ->
            error({badrecord,something})
    end.

and this will eventually become: (use erlc -S)

{function, g, 0, 4}.
  {label,3}.
    {line,[{location,"test.erl",10}]}.
    {func_info,{atom,test},{atom,g},0}.
  {label,4}.
    {move,{literal,{something,undefined,undefined,undefined}},{x,0}}.
    {test,is_tuple,{f,5},[{x,0}]}.
    {test,test_arity,{f,5},[{x,0},4]}.
    {get_tuple_element,{x,0},0,{x,1}}.
    {get_tuple_element,{x,0},3,{x,2}}.
    {test,is_eq_exact,{f,5},[{x,1},{atom,something}]}.
    {move,{x,2},{x,0}}.
    return.
  {label,5}.
    if_end.

The #something.email part of the expression not only means getting the email field of the created record, but also checking that the passed record is well formed. This test is currently not optimized by default. Fortunately, you can optimize it with -compile([inline]). in your module or +inline on the command line.

The following solution is simpler for the compiler:

f() -> element(#something.email, #something{}).

Record syntactic sugar (here #something.email is the index of email field) will expand to:

f() ->
    element(4, {something,undefined,undefined,undefined}).

In this case, we do not tell Erlang to test anything about #something{} being a proper #something record. The compiler always optimizes calls to element/2 built-in function. So this will eventually become:

{function, f, 0, 2}.
  {label,1}.
    {line,[{location,"test.erl",7}]}.
    {func_info,{atom,test},{atom,f},0}.
  {label,2}.
    {move,{atom,undefined},{x,0}}.
    return.

Please note that the default value for any field is undefined unless explicitly provided. As a result, your code:

-record(something, {id                :: integer(),
                    name              :: string(),
                    email = undefined :: string() | undefined}).

is equivalent to:

-record(something, {id    = undefined :: integer() | undefined,
                    name  = undefined :: string()  | undefined,
                    email = undefined :: string()  | undefined}).

Yet, your code seems to mean that id always be an integer() and never undefined, and likewise that name always be a string(). This is untrue. If this is what you mean, you should provide a default value different from undefined:

-record(something, {id    = 0   :: integer(),
                    name  = ""  :: string(),
                    email       :: string()}).

Only providing a default value will tell dialyzer that id and name can never be undefined.




回答2:


Construct empty record and look at some field: (#something{})#something.email.



来源:https://stackoverflow.com/questions/18116330/is-there-a-way-to-do-lookups-on-default-record-values

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!