How can I optimize this?

283 Views Asked by At

I'm currently learning to code Erlang. I have a web application on top of Chicago Boss. I have a model called Todo, and I would like to offer CRUD operations on it as a REST API.

In my PUT method I have this code:

index('PUT', [Id]) ->
    Todo = boss_db:find(Id),
    Body = element(2, mochijson:decode(Req:request_body())),
    %% Set the new values
    NewTodo = Todo:attributes([
            {subject, proplists:get_value("subject", Body)},
            {done, proplists:get_value("done", Body)}
        ])
,
    {json, [{todo, element(2, NewTodo:save())}]}.

How can I optimize this code fragment? Or is this already the best possible?

Is there some "smarter" way to change the keys of a proplist to atom keys? Like this:

[{"subject", "Foo"}] -> [{subject, "Foo"}].

I also find it kind of tedious to assign a Todo variable and then have a NewTodo. Sadly I can't find some good example Erlang Chicago Boss apps on github that I can check out.

3

There are 3 best solutions below

1
On

You always can do something like this:

t([{"subject", V}|T]) -> [{subject, V}|t(T)];
t([{"done"   , V}|T]) -> [{done,    V}|t(T)];
t([_             |T]) ->               t(T) ; % optional garbage ignoring clause
t([])                 -> [].

But I doubt, it will be significant speed improvement in your case.

May be you will be able squeeze last bit from this:

-compile({inline, [t/1]}).
t(L) -> t(L, []).

t([{"subject", V}|T], A) -> t(T, [{subject, V}|A]);
t([{"done"   , V}|T], A) -> t(T, [{done,    V}|A]);
t([_             |T], A) -> t(T, A); % optional garbage ignoring clause
t([], A)                 -> A.

Which is worth only for benchmark code competitions ;-) (Note there is not lists:reverse/1 call in last clause. It would be ruining improvement form previous version.)

P.S.: If you think I'm micro-optimization freak, you are right, so I would replace lists:reverse/1 call with lists:reverse/2 to use BIF directly and save some more time ;-)

0
On

Unfortunately I cannot comment on Hynek's answer, but as Erlang newbie, my first guess would have been to go for something along the lines of:

lists:map(fun({A, B}) -> {list_to_atom(A), B} end, [X || {Y, Z}=X <- List, is_list(Y)]).

You cannot really avoid the NewTodo assignment

0
On

How about

index('PUT', [Id]) ->
    Body = element(2, mochijson:decode(Req:request_body())),
    OldTodo = boss_db:find(Id),
    NewTodo = OldTodo:attributes([ {list_to_atom(A),B} || {A,B}<-Body ]),
    {json, [{todo, element(2, NewTodo:save())}]}.