What alternatives are there to parameterised modules in Erlang? - erlang

I can see why parameterised modules are used so much, as they allow us to do things like:
X = y:new("param").
X:action1().
X.get_property():
: which feels very OO. However, this is only an experimental feature in Erlang and I hear it may be removed, so I need to find an alternative.

Parametrised modules are nothing but a shortcut on the first argument of a function. See these two examples:
-module(y, [Name,Age]).
-compile(export_all).
action1() -> io:format("Hello, ~p!~n",[Name]).
get_age() -> Age.
Run it:
1> c(y).
{ok,y}
2> X = y:new("Fred",22).
{y,"Fred",22}
3> X:action1().
Hello, "Fred"!
ok
4> X:get_age().
22
Now without parametrized stuff:
-module(y).
-compile(export_all).
action1([Name,_]) -> io:format("Hello, ~p!~n",[Name]).
get_age([_,Age]) -> Age.
Running it:
1> c(y).
{ok,y}
2> X = ["Fred",22].
["Fred",22]
3> y:action1(X).
Hello, "Fred"!
ok
4> y:get_age(X).
22
The biggest 'advantage' of parametrized modules is that you shift the burden of carrying state from a variable to a module name. This is in appearance much simpler to someone not used to 'the Erlang way', but it clashes with conventional styles of code.
It's not just a question of being experimental or not. You throw out referential transparency and the semantics to immutable variables become a bit weird. A good example of this is to imagine you add the following functions to a parametrized module:
ret_fn() -> fun(Age) -> Age + 5 end.
When compiling the module, you get the warning ./y.erl:8: Warning: variable 'Age' shadowed in 'fun'. This is warning you that you are using the name of a predefined variable within the head clause of an anonymous function. However, a quick look at the ret_fn/0 function shows absolutely NO sign of where that variable is from.
Now imagine that you use the variable Name for any other purpose; you will get a run time error telling you ** error: no match of right hand side value < ... >.
The point I'm making is that parametrized modules reduce the amount of typing you need to do at the expense of logical simplicity. Not just for you, but for any other Erlang programmer to work on your code.
On top of that, tools like dialyzer, TypEr, tidiers and whatnot have no guarantee to support these idioms. These tools are pretty useful, too! Don't dismiss them. (Edit: newer versions of Erlang (R13B04+) now guarantee that support)
The best alternative to parametrized modules is to avoid them and use what every other Erlang programmer outside of mochiweb is using.

Why can't you use the usual way i.e. send message to a (registered) server process associated with a module?
The message in question can be whatever you want of course e.g. configuration etc.
What problem are you trying to solve that can't be handled in such way?

Related

Why doesn't dialyzer detect this bad type?

Dialyzer behaves rather strangely to me in this case, and I haven't found anything to better understand it.
This is not an error:
defmodule Blog.UserResolver do
#type one_user :: ( {:error, String.t} )
#spec find(%{id: String.t}, any()) :: one_user
def find(%{id: id}, _info) do
age = :rand.uniform(99)
if (age < 100) do
# This doesn't trigger a type error, even though it's wrong
{:ok, %{email: "dw#1g.io", name: "Deedub"}}
else
{:error, "Age isn't in the right range"}
end
end
end
Note that one of the possible return branches definitely does not match with the type signature.
This however does have an error:
defmodule Blog.UserResolver do
#type one_user :: ( {:error, String.t} )
#spec find(%{id: String.t}, any()) :: one_user
# Throws an error since no return path matches the type spec
def find(%{id: id}, _info) do
age = :rand.uniform(99)
if (age < 100) do
{:ok, %{email: "dw#1g.io", name: "Deedub"}}
else
10
end
end
end
In this case, none of the possible branches match the typespec, and dialyzer says has this error message:
web/blog/user_resolver.ex:4: Invalid type specification for function 'Elixir.Blog.UserResolver':find/2. The success typing is (#{'id':=_, _=>_},_) -> 10 | {'ok',#{'email':=<<_:64>>, 'name':=<<_:48>>}}
The part I don't understand is that dialyzer clearly recognizes the two different types the branches may return ((#{'id':=_, _=>_},_) -> 10 | {'ok',#{'email':=<<_:64>>, 'name':=<<_:48>>}), so it's not a problem of inference. So why then does it not recognize that one of the branches does not conform to the type spec (it seems to be happy if just one of the branches conforms, which is not what I want at all)
From the LearnYou link that Dogbert provided, dialyzer will:
only complain on type errors that would guarantee a crash.
In your first example, if age is always greater than or equal to 100, your function will return the declared type. In the second example, there is no way for your function to return the declared type.
dialyzer creates a set of constraint equations. If there is any solution to those equations, then dialyzer won't complain. Erlang was created as a dynamically typed language. dialyzer is just a program someone wrote after the fact. For reasons I'm sure they pondered over and discussed and theorized about, the designers of dialyzer chose that functionality.
I'm looking for a more rigorous type checker if possible.
Not possible so far:
The Erlang Type System
The reason for not having a more elaborate type system is that none of
the Erlang inventors knew how to write one, so it never got done. The
advantage of a static type system is that errors can be predicted at
compile time rather than at runtime, therefore allowing faults to be
detected earlier and fixed at a lower cost. A number of people have
tried to build a static type system for Erlang. Unfortunately, due to
design decisions taken when Erlang was invented, no project has been
able to write a comprehensive type system, since with hot code
loading, this is intrinsically difficult. To quote Joe Armstrong in
one of the many type system flame wars, "It seems like it should be
'easy'—and indeed, a few weeks programming can make a type system that
handles 95% of the language. Several man-years of work [by some of the
brightest minds in computer science] have gone into trying to fix up
the other 5%—but this is really difficult."
From "Erlang Programming (Francesco Cesarini & Simon Thompson)".
A test suite is required to keep dynamically typed programs under control. Elixir is just a Rubified version of Erlang. Ruby is also a dynamically typed language--but it has no dialyzer. The only thing Ruby has is testing. You use test suites to keep the Wild West of computer programming languages under control--not a compiler. If you require a statically typed language, then a Rubified version of Erlang wasn't a great choice--see Haskell.

Is a 'standard module' part of the 'programming language'?

On page 57 of the book "Programming Erlang" by Joe Armstrong (2007) 'lists:map/2' is mentioned in the following way:
Virtually all the modules that I write use functions like
lists:map/2 —this is so common that I almost consider map
to be part of the Erlang language. Calling functions such
as map and filter and partition in the module lists is extremely
common.
The usage of the word 'almost' got me confused about what the difference between Erlang as a whole and the Erlang language might be, and if there even is a difference at all. Is my confusion based on semantics of the word 'language'? It seems to me as if a standard module floats around the borders of what does and does not belong to the actual language it's implemented in. What are the differences between a programming language at it's core and the standard libraries implemented in them?
I'm aware of the fact that this is quite the newby question, but in my experience jumping to my own conclusions can lead to bad things. I was hoping someone could clarify this somewhat.
Consider this simple program:
1> List = [1, 2, 3, 4, 5].
[1,2,3,4,5]
2> Fun = fun(X) -> X*2 end.
#Fun<erl_eval.6.50752066>
3> lists:map(Fun, List).
[2,4,6,8,10]
4> [Fun(X) || X <- List].
[2,4,6,8,10]
Both produce the same output, however the first one list:map/2 is a library function, and the second one is a language construct at its core, called list comprehension. The first one is implemented in Erlang (accidentally also using list comprehension), the second one is parsed by Erlang. The library function can be optimized only as much as the compiler is able to optimize its implementation in Erlang. However, the list comprehension may be optimized as far as being written in assembler in the Beam VM and called from the resulted beam file for maximum performance.
Some language constructs look like they are part of the language, whereas in fact they are implemented in the library, for example spawn/3. When it's used in the code it looks like a keyword, but in Erlang it's not one of the reserved words. Because of that, Erlang compiler will automatically add the erlang module in front of it and call erlang:spawn/3, which is a library function. Those functions are called BIFs (Build-In Functions).
In general, what belongs to the language itself is what that language's compiler can parse and translate to the executable code (or in other words, what's defined by the language's grammar). Everything else is a library. Libraries are usually written in the language for which they are designed, but it doesn't necessarily have to be the case, e.g. some of Erlang library functions are written using C as Erlang NIFs.

Erlang: Will adding type spec to code make dialyzer more effective?

I have a project that doesn't have -spec or -type in code, currently dialyzer can find some warnings, most of them are in machine generated codes.
Will adding type specs to code make dialyzer find more errors?
Off the topic, is there any tool to check whether the specs been violated or not?
Adding typespecs will dramatically improve the accuracy of Dialyzer.
Because Erlang is a dynamic language Dialyzer must default to a rather broad interpretation of types unless you give it hints to narrow the "success" typing that it will go by. Think of it like giving Dialyzer a filter by which it can transform a set of possible successes to a subset of explicit types that should ever work.
This is not the same as Haskell, where the default assumption is failure and all code must be written with successful typing to be compiled at all -- Dialyzer must default to assume success unless it knows for sure that a type is going to fail.
Typespecs are the main part of this, but Dialyzer also checks guards, so a function like
increment(A) -> A + 1.
Is not the same as
increment(A) when A > 100 -> A + 1.
Though both may be typed as
-spec increment(integer()) -> integer().
Most of the time you only care about integer values being integer(), pos_integer(), neg_integer(), or non_neg_integer(), but occasionally you need an arbitrary range bounded only on one side -- and the type language has no way to represent this currently (though personally I would like to see a declaration of 100..infinity work as expected).
The unbounded-range of when A > 100 requires a guard, but a bounded range like when A > 100 and A < 201 could be represented in the typespec alone:
-spec increment(101..200) -> pos_integer().
increment(A) -> %stuff.
Guards are fast with the exception of calling length/1 (which you should probably never actually need in a guard), so don't worry with the performance overhead until you actually know and can demonstrate that you have a performance problem that comes from guards. Using guards and typespecs to constrain Dialyzer is extremely useful. It is also very useful as documentation for yourself and especially if you use edoc, as the typespec will be shown there, making APIs less mysterious and easy to toy with at a glance.
There is some interesting literature on the use of Dialyzer in existing codebases. A well-documented experience is here: Gradual Typing of Erlang Programs: A Wrangler Experience. (Unfortunately some of the other links I learned a lot from previously have disappeared or moved. (!.!) A careful read of the Wrangler paper, skimming over the User's Guide and man page, playing with Dialyzer, and some prior experience in a type system like Haskell's will more than prepare you for getting a lot of mileage out of Dialyzer, though.)
[On a side note, I've spoken with a few folks before about specifying "pure" functions that could be guaranteed as strongly typed either with a notation or by using a different definition syntax (maybe Prolog's :- instead of Erlang's ->... or something), but though that would be cool, and it is very possible even now to concentrate side-effects in a tiny part of the program and pass all results back in a tuple of {Results, SideEffectsTODO}, this is simply not a pressing need and Erlang works pretty darn well as-is. But Dialyzer is indeed very helpful for showing you where you've lost track of yourself!]

Does Erlang have methods?

E.g. is there something like this:
O = widget:new(),
O:whirl()
I seem to recall seeing some code like this (maybe I was imagining it), but I haven't seen it in the tutorials that I've read. From what I've seen, the closest thing is this:
O = widget:new(),
widget:whirl(O)
That's not too bad, but not having to repeat widget: in the second expression would be nice.
This is syntax for parametrized modules which was removed from Erlang in R16 (2012).
No, Erlang does not have methods. Erlang has processes, not objects, and you communicate with them by messaging them, not calling methods on them. That's it. That's all there is to it.
The closest thing to new in Erlang that means what it means in Java or C++ is spawn. (The parameterized module discussion touches on something very different from what you would expect coming from a C++ type language where new reserves memory, calls a constructor, etc.)
There are actually two aspects to this: data objects (like a dict or list or something) and processes (things you create with spawn).
Within a function definition you might see something like
SomeDict = dict:new(),
or
OtherDict = dict:from_list(KV_List)
This does indeed create something, but its not an "object" in the Java or C++ sense, it is an "object" in the (older) sense of the term that it is a named reference to something in memory. And indeed you interact with it the same way you demonstrated above:
D = dict:new(),
ok = some_operation(D),
where some_operation/1 might be anything, whether it is dict:foo() or something else. The part before the colon is a module identifier, telling the runtime what namespace that function you're calling exists in -- nothing more.
The other thing, spawn, is much more like new in C++, where you want to create a complete thing that is alive and has arms and legs -- a noun that can do verby things:
Pid = spawn(Mod, Fun, Args),
Pid ! {some, message},
Most of the time you only see the erlang:send/2 function (also written as the infix ! operator) in prototype or non-OTP code. Usually this is hidden from you by interface functions that abstract away the fact that you are sending async messages all the time to communicate with your processes.
For some more in-depth explanation, I recommend reading Learn You Some Erlang -- the author explains this and other basic concepts in some depth.
Whatever you do, do not fall into the simpleton's trap of thinking Erlang is Java. That is just a great way to trip over your own preconceptions and get frustrated. This is probably the #1 beginner mistake I see people make...

Specifications for functions: -spec. Efficiently usage

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.
-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.
-spec's for functions are specifications which has several places where they help:
They act as documentation of the function. Generating EDoc will pull the specs and make them available in the documentation.
They are a specification for the dialyzer. When the dialyzer runs it will use the specs to determine if the code is wrong in any way. That is, if you spec is wrong - and in some cases it will help the system to understand exactly why the code is wrong too.
They are a valuable tool in the specification of behaviours. There is a new -callback keyword which can be used to do this for behavioural APIs.
They are valuable for constructing a type skeleton of how the program fits together and from where data comes from.
Together with the cousins -type and -opaque you can force certain types to be opaque to pieces of code. That means you are not allowed to see the internal representation on a static verification level. This can in turn help drive modularized code as you are not allowed to tightly couple code pieces.

Resources