Reading Module Attributes in Erlang
July 24, 2008 11:20 pm Erlang, Programming, Tools and Libraries[digg-reddit-me]Oddly, the Erlang standard library does not have a function for reading out module attributes. One is relatively straightforwardly hacked together with beam_lib, if you know what you’re doing, but most people wouldn’t know to look there, and it’s kind of a hassle to write.
So, I wrote it for you.
- get_module_attribute is a member of the ScUtil library.
- This closes issue 117.
Usage is straightforward: scutil:get_module_attribute(lists, export).
get_module_attribute(Module,Attribute) ->
case beam_lib:chunks(Module, [attributes]) of
{ ok, { _, [ {attributes,Attributes} ] } } ->
case lists:keysearch(Attribute, 1, Attributes) of
{ value, {Attribute,[Value]} } -> Value;
false -> { error, no_such_attribute }
end;
{ error, beam_lib, { file_error, _, enoent} } ->
{ error, no_such_module }
end.

July 25th, 2008 at 12:32 am
MyMod:module_info(attributes) ?
July 25th, 2008 at 1:26 am
No. If you want, for example, the author attribute of the testerl module, you would write:
scutil:get_module_attribute(testerl, author).
July 25th, 2008 at 11:56 am
This really should be in the standard library.
July 25th, 2008 at 6:13 pm
Chaneisha: I agree.
July 25th, 2008 at 7:06 pm
module_info(attributes))returns a proplist. You can get the value of theauthorattribute withAuthors = proplists:get_all_values(author, module:module_info(attributes))..The same applies to other attributes and can be used to retrieve compile details as well. With
SourceFile =proplists:get_value(source, complete:module_info(compile))).you can retrieve the source file name for a module.You can have the same attribute in a module more than once (very good for identifying multiple authors). Based on my tests
scutil:get_module_attribute/2throws an exception in this case.Unfortunately beam_lib is also not savvy to the code path because it works directly on BEAM files. As a result if you are loading modules outside the current working directory
scutil:get_module_attribute/2will return{error, no_such_module}.Thank you for sharing scutil, it has some neat features (I particularly like
type_of/1).Grassroots Open Source activity like this in the Erlang community is hugely important. Keep up the good work!
July 26th, 2008 at 6:07 pm
Mr O’Dea: Your commentary regarding get_module_attribute is well heeded, and I’ll consider what to do.
As far as scutil, don’t consider it released until you look at the project tracker list; there are more than 200 functions involved. I’m moving them one at a time in order to get a chance to clean things up somewhat and to get people like you to tell me the things I didn’t know, so that I have some chance of keeping up with the changes as such required.
Incidentally, you can find all the scutil resources at scutil.com. Also, there are several more libraries coming in the near future.
In general, I appreciate the kind commentary, but in the somewhat inappropriately chosen words of ODB, “Nigga, I’s just gettin started.”
July 26th, 2008 at 7:56 pm
Here’s a drop-in replacement for
scutil:get_module_attribute/2that preserves the return values:If you are willing to change the return values
scutil:get_module_attribute/2you can take let it crash approach:The let it crash approach leaves it up to the user to interpret thrown exceptions when they send in garbage like non-atoms for the parameters, or non-existent modules. Let it crash is standard practice for library development in Erlang and Java. In both languages it is considered a best practice for libraries to let exceptions through so that client code can decide how to recover.
July 27th, 2008 at 11:52 am
I confess, this is one of the parts of the Erlang mindset that confounds me.
In what way would removing the error reporting make handling the error easier to tolerate? Provided as stands now, if you want it to propogate up the ownership tree (or other ownership arrangement), just don’t put an {error,E} clause in your pattern match, and your crash will be generated.
The germane issues that led me to chose to handle the errors in code are three:
1) Handling the error upstream by way of the exception generated is cumbersome and requires the user to learn to read the beam_lib errors
2) Having the error handled as a straightforward tuple makes both the library code and the dependant code substantially easier to read and to reason about
3) This allows me to draw a clear line in the sand between expected errors and unexpected errors
There’s a whole lot of kool-aid drinking going on in the Erlang community. In my path through the various languages I speak, I’ve found one of my most important behavioral traits is the combination of the willingness to ask what the underlying benefit is to a behavior, combined with my willingness to put in the time to find people who can explain, ask them, and really work over their responses.
Indeed, the Erlang standard library itself seems pretty divided on the issue – note the response from primitives like whereis(not_a_pid).
So, let me be the first. What value does allowing it to crash in the library provide which is not equivalent to a user declining to handle managed errors, and which is superior to those things mentioned above?
Simply put, what specific good does it do to not handle them?
July 28th, 2008 at 10:41 am
John, you are right. It is helpful to handle these errors in the library and provide some meaningful error to the user. How about using
exit(no_such_attribute)instead of{error, no_such_attribute}?.Returning errors as values has tangly consequences:
Author = scutil:get_module_attribute(lists, author).
Here
Authoris bound to{error, no_such_attribute}. How does the client-code decide that Author is an error here? How does client-code decide in general that a return value fromscutil:get_module_attribute/2is an error?July 28th, 2008 at 11:25 am
Here I would argue that returning it as a value is in fact a significant boon to identifiability. Returning it as an exception is essentially context free – the exception could be bubbling up from any child process, and there’s no particularly useful way to label the exceptions to distinguish one from another.
On the balance, I honestly feel that context in the direct case is fairly clear:
TestSuite = scutil:get_module_attribute(YourModule, testsuite).
If TestSuite’s contents are {error,no_such_module} or {error,no_such_attribute}, it’s in each case pretty clear what happened: in the first case, the user referred to a module that didn’t exist (probable typo), and in the second case, the module isn’t set up for that testing rig.
Er. I worry that I’m misunderstanding the question. If you’re asking how a client safely uses the function, there are two approaches: to handle common errors it’s just
case scutil:get_module_attribute(SomeModule, SomeAttribute) of
{error,E} -> {error,E};
ResultValue -> do_stuff_with(Answer)
end.
And if you prefer the let it crash mindset, it’s just
Foo = scutil:get_module_attribute(Bar,Baz),
do_something_requiring_an_atom(Foo).
The germane issue here in my mind is that returning clean sensible errors makes it very obvious what’s going on, requires no special code upstream, allows the user to have the let it crash mindset or the handle obvious problems mindset, and presents a much clearer difference between normal things being dumb and oh my god something caught fire this never happens kind of errors.
I understand that in the primary sense, the let it crash mindset coupled with code loading and crash tolerance lead to fault resistant systems from which defects are removed early due to kersplosions. That said, I just don’t believe this is such a case, any more than having whereid(not_a_pid) crashing would be.
Maybe I’m wrong. If I am, please, help me see the active value of using a crash to communicate this. I understand how to do it. What I need to understand is why.
July 28th, 2008 at 12:51 pm
Wow. I really went off base on this. For distributed errors exceptions make sense, but not for library errors.
What do you think of returning good results in the form
{ok Result}? Functions likefile:open/2andio:read/1both yield{ok, Result}|{error, Error}. This makes an pattern matching exception occur at the point of invocation. This avoids the need for a explicit check of the results using a case statement.Scrap my nonsense about exceptions and let it crash. It doesn’t apply here
July 28th, 2008 at 7:09 pm
That’s a good idea. That also removes ambiguity in the tremendously unlikely case that the actual contents of an attribute are {error,E}.
Not at all. I learned several things to keep in consideration for other parts of my libraries for this conversation, and I’m quite glad you helped me as such. Thank you.