[erlang-questions] Dynamically access record fields

Dmitry Belyaev <>
Tue Feb 12 13:43:07 CET 2013


> g(bindings, #http_req{bindings=Ret}) -> Ret;
> g(body_state, #http_req{body_state=Ret}) -> Ret;
> g(buffer, #http_req{buffer=Ret}) -> Ret;
> g(connection, #http_req{connection=Ret}) -> Ret;
> g(cookies, #http_req{cookies=Ret}) -> Ret;
> g(fragment, #http_req{fragment=Ret}) -> Ret;
> g(headers, #http_req{headers=Ret}) -> Ret;
> g(host, #http_req{host=Ret}) -> Ret;
> g(host_info, #http_req{host_info=Ret}) -> Ret;
> g(meta, #http_req{meta=Ret}) -> Ret;
> g(method, #http_req{method=Ret}) -> Ret;
> g(multipart, #http_req{multipart=Ret}) -> Ret;
> g(onresponse, #http_req{onresponse=Ret}) -> Ret;
> g(p_headers, #http_req{p_headers=Ret}) -> Ret;
> g(path, #http_req{path=Ret}) -> Ret;
> g(path_info, #http_req{path_info=Ret}) -> Ret;
> g(peer, #http_req{peer=Ret}) -> Ret;
> g(pid, #http_req{pid=Ret}) -> Ret;
> g(port, #http_req{port=Ret}) -> Ret;
> g(qs, #http_req{qs=Ret}) -> Ret;
> g(qs_vals, #http_req{qs_vals=Ret}) -> Ret;
> g(resp_body, #http_req{resp_body=Ret}) -> Ret;
> g(resp_headers, #http_req{resp_headers=Ret}) -> Ret;
> g(resp_state, #http_req{resp_state=Ret}) -> Ret;
> g(socket, #http_req{socket=Ret}) -> Ret;
> g(transport, #http_req{transport=Ret}) -> Ret;
> g(version, #http_req{version=Ret}) -> Ret.

It is possible to write:

-record(record1, {a, b, c}).
-record(record2, {c, b, a}).

g(Field, #record1{} = Record) ->
    g(Field, Record, record_info(fields, record1), 2);
g(Field, #record2{} = Record) ->
    g(Field, Record, record_info(fields, record2), 2).

g(Field, Record, [Field | _], I) ->
    element(I, Record);
g(Field, Record, [_ | Fields], I) ->
    g(Field, Record, Fields, I + 1);
g(_, _, _, _) ->
    error(badarg).


-- 
Dmitry Belyaev

On 12.02.2013, at 6:04, Loïc Hoguin <> wrote:

> On 02/12/2013 02:06 AM,  wrote:
>> "Loïc Hoguin" <> wrote
>> 
>>> - Micro optimization. Most applications wouldn't care for it either
>>   Applications cannot care or not care for anything.
>>   If people are willing to pay the price, fine;
>>   my problem was that I saw no evidence that any proponent
>>   realised that there _was_ a price "I just don't see the harm".
> 
> Pedantic point. If execution speed isn't an application requirement, then there's no need to even concern yourself about it, because everything else is more important.
> 
> You'll also most likely improve the execution speed of your system by improving inter-process communication and application architecture rather than having to execute one less instruction here and there.
> 
>>> - That's a developer's choice
>>   Making code less readable may be a developer's _choice_,
>>   but if anyone else might be required to read the code,
>>   it isn't a developer's _right_.
> 
> You seem to think code readability isn't subjective.
> 
>>> - BS, you have the same issue with Var = #rec.field, no difference there
>>   Reducing the effectiveness of type checking?
>>   Yes, we do have the same issue with Var = #rec.field,
>>   BUT I don't say that using that is a good idea either.
> 
> Sometimes there's no good solution and you still have to get things done. However, as far as "bad" solutions go, #rec.field is worse than 'field', because you'll get a meaningless integer when you output the value when debugging, instead of what could have been an atom.
> 
>>> - See previous point
>>   Inadvertently providing wider access?  No, this one is
>>   quite different from #rec.field.  You can only use
>>   #rec.field within the compile-time scope of a -record
>>   declaration for 'rec'.  If #rec.Field were allowed,
>>   Field could be created at a point where the record
>>   declaration was _not_ visible.  #rec.field has to be
>>   inside the module; use of it is not inadvertent; and
>>   you can _find_ it trivially with a text editor, so
>>   you can very easily check which fields might be accessed
>>   that way.  As far as I can see, that's not true of
>>   #rec.Field, where Field comes from some unknown arbitrary
>>   module.
> 
> Well, #rec.Field is not possible to begin with, and wouldn't make sense for records. But it would definitely be good for another data type, as I'll explain.
> 
>> The key point is that we just plain DON'T NEED #rec.Field;
>> anything we could do with it can be done without it more
>> clearly using existing mechanisms, with better control over
>> access and all sorts of other programmer-liked goodness.
> 
> There's nothing clear in having to write:
> 
> g(bindings, #http_req{bindings=Ret}) -> Ret;
> g(body_state, #http_req{body_state=Ret}) -> Ret;
> g(buffer, #http_req{buffer=Ret}) -> Ret;
> g(connection, #http_req{connection=Ret}) -> Ret;
> g(cookies, #http_req{cookies=Ret}) -> Ret;
> g(fragment, #http_req{fragment=Ret}) -> Ret;
> g(headers, #http_req{headers=Ret}) -> Ret;
> g(host, #http_req{host=Ret}) -> Ret;
> g(host_info, #http_req{host_info=Ret}) -> Ret;
> g(meta, #http_req{meta=Ret}) -> Ret;
> g(method, #http_req{method=Ret}) -> Ret;
> g(multipart, #http_req{multipart=Ret}) -> Ret;
> g(onresponse, #http_req{onresponse=Ret}) -> Ret;
> g(p_headers, #http_req{p_headers=Ret}) -> Ret;
> g(path, #http_req{path=Ret}) -> Ret;
> g(path_info, #http_req{path_info=Ret}) -> Ret;
> g(peer, #http_req{peer=Ret}) -> Ret;
> g(pid, #http_req{pid=Ret}) -> Ret;
> g(port, #http_req{port=Ret}) -> Ret;
> g(qs, #http_req{qs=Ret}) -> Ret;
> g(qs_vals, #http_req{qs_vals=Ret}) -> Ret;
> g(resp_body, #http_req{resp_body=Ret}) -> Ret;
> g(resp_headers, #http_req{resp_headers=Ret}) -> Ret;
> g(resp_state, #http_req{resp_state=Ret}) -> Ret;
> g(socket, #http_req{socket=Ret}) -> Ret;
> g(transport, #http_req{transport=Ret}) -> Ret;
> g(version, #http_req{version=Ret}) -> Ret.
> 
> Instead of
> 
> g(Field, Req) -> Req#http_req.Field.
> 
> Better control over access? You can still do it in the guard if you need to.
> 
> I'm sure you'll tell me this should be generated? That's neither clear nor practical. And the generated code would likely not give good control over access either anyway.
> 
>> Erlang needs dynamic field access like oysters need shooting-sticks.
> 
> For records I agree, for almost anything else (dict | sets | proplists | maps | ...) I don't. Especially more so in Erlang.
> 
> You're being very affirmative over what people do or don't need, but you seem to forget the one thing that people need the most: getting things done. Perhaps a solution is slightly more prone to some kinds of bugs, however making it harder to do something is as likely to create bugs. Erlang is wonderful in that you can easily contain bugs to the local process or group of processes, so I don't really see why we wouldn't want to have more productive solutions. (And yes, productivity is also subjective.)
> 
> -- 
> Loïc Hoguin
> Erlang Cowboy
> Nine Nines
> http://ninenines.eu
> _______________________________________________
> erlang-questions mailing list
> 
> http://erlang.org/mailman/listinfo/erlang-questions




More information about the erlang-questions mailing list