Reputation: 4916
Do Erlang type specifications belong in .hrl files(which are loaded into any .erl file that needs them) or should they be kept in an Erlang module, and then exported (which would allow them to be used in other modules)? It seems to me that both methods allow achieve the same thing.
Thanks in advance!
Upvotes: 4
Views: 815
Reputation: 1660
Type specifications (i.e. -spec
attributes) belong to the module where the function is defined. Period.
Type definitions (-type
, -opaque
), on the other hand, could be defined in .hrl
files, but I think this is usually a poor decision. This would mean, that every module including the header in question would "define" the type locally. This might lead to namespace clashes, when the module already defined a type which is also defined in a header you wanted to include.
Exporting types from modules instead of defining them in .hrl
s gives you a namespace prefix and disambiguates between types defined locally (mytype()
) and by external applications/modules (yourmod:yourtype()
or, sic!, yourmod:mytype()
).
Usually, when writing an Erlang application or library it's best to define the type in the module that uses it (the most). For types which are exported outside the library, export them from the main library module - if the app is called myapp
, then make all public types accessible like myapp:config()
, myapp:some_record()
.
One more thing comes to my mind: Dialyzer doesn't like bare record definitions - it's advised to explicitly define types for records (so one -type
for each -record
). On the other hand, it's convenient to place record definitions in header files, so they can be shared between code in different places (like src/mymod.erl
and test/mymod_tests.erl
). In such case I'd define the record in the header file (src/mymod.hrl
for a private module or include/mymod.hrl
if the module is part of the application/library public interface), but still define and export the type from the module where it belongs (i.e. mymod:some_record()
).
The point tkowal raised is also important. If you don't want to expose the internal structure, then the -opaque
attribute is meant to do just that - it says "don't depend on this type's internal structure". So you just need to define a type with -opaque
instead of -type
, export it, and let Dialyzer warn you about each place in the code which accidentally builds a term of that type but doesn't state it explicitly, or about each pattern matching which tries to deconstruct that type outside the module where the type is defined.
Upvotes: 7
Reputation: 2496
It does matter a bit in that specs are actually used to generate a function/module's documentation with the edoc application.
And if the right spec clause is not above the right function clause edoc will complain and the whole module's documentation generation will fail.
Please think of edoc.
Upvotes: 2
Reputation: 9289
It doesn't matter that much. You can put them, where it makes sense.
If you put something in .hrl and other modules depend on it - it is harder to change. When you put it in module, it should be easier to modify and refactor.
Upvotes: 1