【问题标题】:Is there a way to do lookups on default record values?有没有办法对默认记录值进行查找?
【发布时间】:2025-12-18 23:55:01
【问题描述】:

给定一个记录

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

有没有办法获取字段的默认值,在这个例子中得到#something.email 默认为未定义的事实?

【问题讨论】:

    标签: erlang records


    【解决方案1】:

    记录是 Erlang 中的语法糖,由编译器扩展。 @Dmitry 建议的以下解决方案有效,但除非您传递 +inline,否则编译器不会优化它,因为这里的技巧是真正创建记录:

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

    这样的记录语法糖将被扩展为:(使用erlc -E

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

    这最终会变成:(使用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.
    

    表达式的#something.email 部分不仅表示获取创建记录的电子邮件字段,还表示检查传递的记录是否格式正确。默认情况下,此测试当前未优化。幸运的是,您可以在模块中使用-compile([inline]). 或在命令行中使用+inline 对其进行优化。

    以下解决方案对于编译器来说更简单:

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

    记录语法糖(这里#something.email 是电子邮件字段的索引)将扩展为:

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

    在这种情况下,我们不会告诉 Erlang 测试任何关于 #something{} 是否是正确的 #something 记录的内容。编译器总是优化对element/2 内置函数的调用。所以这最终会变成:

    {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.
    

    请注意,任何字段的默认值都是undefined,除非明确提供。结果,您的代码:

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

    相当于:

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

    然而,您的代码似乎意味着 id 始终是 integer() 而不是 undefined,同样,名称始终是 string()。这是不真实的。如果这是你的意思,你应该提供一个不同于undefined的默认值:

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

    仅提供默认值会告诉dialyzer id 和 name 永远不能是undefined

    【讨论】:

      【解决方案2】:

      构造空记录并查看某个字段:(#something{})#something.email

      【讨论】: