2015-03-18
One of the things I found myself wishing for in Erlang was labeled
arguments. Even with common operations from stdlib (like
lists:foldl
) - I constantly forget the correct order of
arguments, and it becomes that much more error-prone in complex cases
(see
cowboy_req:new/14
,
for instance).
Another desirable feature is optional arguments, which are assigned a default value when omitted.
It turns-out that the semantics of these requirements are fully met by records!
I only realized this when lamenting the lack of the same features in SML (as compared to OCaml, which does have them) and being pointed to an alternative - anonymous records.
However, proliferation of public record definitions is a bit
problematic in Erlang, due to lack of features for managing name
spaces, but even that can be resolved by following the same technique
that is already used to solve this problem in modules: fully qualify
any public name, i.e. foo_bar_baz
, not just
baz
. To tidy things up even more, I think it is nice to
keep public record definitions in dedicated .hrl
files,
i.e. foo_bar.hrl
, foo_baz.hrl
, not just
records.hrl
.
module.erl
:
-module(module). -include("module_args.hrl"). -export_type( [ args_foo/0 ]). -export( [ foo/1 ]). -type args_foo() :: #module_args_foo{}. -spec foo(args_foo()) -> ok. foo(#module_args_foo { bar = Bar , baz = Baz , qux = Quxicles } ) -> ok = do_stuff_with_quxicles(Quxicles, Bar, Baz).
module_args.hrl
:
-record(module_args_foo, { bar = <<"default">> :: binary() , baz :: integer() , qux = [] :: [stuff()] }).
This may be too heavy of a sledge hammer for most cases, but when your function really can't help but need many arguments - this technique can aid understanding at the cost of some inconvenience.
I used this technique extensively in my OAuth 1 server protocol implementation.
Love it? Hate it? Let me know!