How could I use -spec
word in erlang?
Please give me an idea of efficient usage of this word. Does is stands for documentation purposes only?
I'm try to apply a constraint to function in module by function type specification using -spec
, but I've failed - no restrictions have been applied.
The two main measures for the efficiency of an algorithm are time complexity and space complexity, but they cannot be compared directly. So, time and space complexity is considered for algorithmic efficiency. An algorithm must be analyzed to determine the resource usage of the algorithm.
I have to swap numbers in an array 'd' times, so that left rotation of the array can be done. 'd' is the number of rotations of the array. Suppose if the array is 1->2->3->4->5 and if the d=1 then after one left rotation the array will be 2->3->4->5->1.
A requirements document defines what is needed from the product. It states the product's purpose and what it must achieve. It does not define how to deliver or build what is needed.
-spec
attributes are indeed treated by the compiler and the runtime system as documentation. You cannot add any "executable features" to your code using them and the same applies for -type
and -opaque
attributes.
However they are useful as:
Documentation: they used by EDoc to generate all different forms of documentation for your code. -spec
attributes are function signatures which, depending on how much effort you put into them, can make your code more understandable and maintainable. Suppose that your favorite data structure this month is dict()
. Consider the following code:
my_function(SomeArg, SomeOtherArg, Dict) ->
...
dict:find(SomeKey, Dict)
...
The variable that is being used as a dict has been named as such. But let's say that you have the following snippet:
my_other_function(NamesDict, PlacesDict) ->
...
R1 = my_function(A, B, NamesDict),
...
R2 = my_function(C, D, PlacesDict),
...
Trying to keep up with this might soon lead to code that repeats this Dict
suffix. Even more, you might not even want to remember in the context of my_other_function
that the two arguments are dict()
. So instead you might want to do this:
-spec my_other_function(dict(), dict()) -> atom().
my_other_function(Names, Places) ->
...
R1 = my_function(A, B, Names),
...
R2 = my_function(C, D, Places),
...
Now it is clear that these arguments should be dict() for the function to work and hopefully everyone will be able to figure that without going deep into the code. But suppose you are using this Name
dict()
in other places and it stores some particular information that is exposed with different APIs. Then it's a perfect candidate for a -type
declaration:
-type names() :: dict().
-spec my_other_function(names(), places()) -> atom().
my_other_function(Names, Places) ->
...
R1 = my_function(A, B, Names),
...
R2 = my_function(C, D, Places),
...
If somebody else makes frequent use of this particular data structure you may want to export it too:
-module(my_module).
-export_type([names/0]).
-type names() :: dict().
Other modules can now refer to this particular data structure:
-module(my_other_module).
-record(my_state, {names :: my_module:names(),
...}).
Finally if you would prefer other developer to not inspect this data structure in any way in their modules, you can declare it as -opaque
. Again, this is a "friendly suggestion", as is all the rest of the stuff so far. Or is it...?
Discrepancy detection: If you take time to use -specs
and -types
you would very much like that these are kept up to date. It is common knowledge that nobody maintains the documentation up to date if there is none watching! Luckily, Dialyzer is watching. Dialyzer can check that in all calls to my_function()
the arguments are dict()
(it can do this even without your -spec
annotations but it's so easier if there are these there too) and scream bloody murder if you call it with something else. It can moreover keep track of these exported types and even report opacity violations. So it's not "just documentation".
Testcase generation: PropEr can use the -spec
and -type
definitions to automatically check your functions with random testcases. It is capable to make random testcases even from declarations like this one:
-type int_tree() :: {node, integer(), tree(), tree()} | nil.
The brand new way to specify a set of callbacks for a behaviour is by using the familiar -spec
syntax. Compiler, Dialyzer and possibly other tools can use this information to check a behaviours implementation. See more in the OTP behaviours code and here
Read more here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With