Once I had to change jobs. Before that, I only worked with languages like Python, C ++, C # and a couple of similar ones. And now I had to start working with a functional language. First impressions were "yes, what a garbage?". However, I managed to quickly adapt. Next, I will talk about the main points that I had to get used to or which I had to understand in order to start writing quickly and adequately.
1.
Pattern matchingThis is one of the main features of the language. But to fully understand it, until you begin to really write in the language a little difficult. The first thing I read about pattern matching is that thanks to this thing, you can simply extract data from structures and associate them with variables. But in reality everything is more complicated. Pattern matching works almost everywhere in a given language. I will give three examples illustrating the main applications that I use almost constantly.
Example 1. By definition:
')
{tuple_item_1, tuple_item_2} = tuple
- breaks the 2-element tuple into two variables, which can then be used.
[head_item | tail_list] = list
- splits the list into the first item in the list and the list without the first item.
Example 2. Matching in case:
case get_elem(struct) do {:ok, elem} -> … {:error, reason} -> … end
The
get_elem (struct) function returns a tuple, and the case allows you to immediately extract the data and select a further sequence of actions.
Example3. Feature Mapping:
def function_1(params, :ok) do end def function_1(params, :error) do end def function_1(params, _) do end
Here, in essence, the same function takes two parameters. Pattern matching allows you to choose which function to perform.
A little about the work of pattern matching. Comparison is always top-down. In this example, when calling
function_1 from two parameters, it will first check that the second parameter is equal to
: ok . If the first check fails, it will check for
: error . And if not again, we will in any case enter the third variant of the method. The underscore means "any data", and also the fact that the incoming data does not interest us, that is, we will not use them. If
function_1 (params, _) were first in the list, the program would always select it, and the other two methods would never work. If the desired pattern is not found, an exception will be raised.
2.
PipelineThese are constructions of the following type:
param_1 |> func_1() |> func_2(param_2) …
At first glance, it seems some kind of garbage. But it is worth remembering that Elixir is a functional language. And in a functional language it is quite normal to do the calculations of a function of a function, without intermediate variables. Pipeline is just a convenient entry. The language itself asks for clarity to start the pipeline with a variable or value.
The example above can be rewritten as follows:
func_2(func_1(param_1), param_2)
In other words, the pipeline redirects the result of the calculation of the previous function to the next function by the first argument.
3.
Lack of cyclesThere are no cycles in the Elixir. This fact caused the greatest shock to me, and it is the most difficult to understand. Next comes my opinion and vision, which may not coincide with reality and theory.
The roots of this fact are in the functional programming paradigm, one of the provisions of which states that the result of the program is the function that can call other functions and the program does not assume the storage of intermediate states. Cycles, in turn, are intended to repeatedly change the state external to the cycle.
Replacing the cycles are 2 things - recursion and library methods of working with enumerable elements of the language.
A little more about the little things.1. In Elixir there are no classes, but there are contexts. Essentially, contexts in some way replace classes. The closest description of the context is the eyes of the b-sharper: contexts are something between a class and a namespace in Sharpe, and the context is much closer to the namespace.
2. Atoms. In Elixir there is such a thing as an atom. An atom is essentially something like a “tag.” The easiest way to treat them as a special line. In the examples of this article, there were already two atoms
:: ok,: error . Thanks to atoms, it is much easier to perform pattern matching, and complex logical constructs. In essence, these are constants whose value is their name. An atom always has a ":" in front of the name.
3. How to read method headers correctly. In Elixir, it is customary to designate methods as follows (this is often seen in the documentation):
& function / 2 . It is read as a method with the name “function” and arity 2. Arity is the number of arguments taken.
What helped me to join the language.First, this is the reference book on the android “Elixir Tutorial”. It is good in that it briefly covers the main points of the language and its syntax, and it can be read on the bus. Minus: it is in English, so not everyone is suitable.
Secondly, the book "Introduction to Elixir" by Senloren S., Eisenberg D ... This book shows the techniques of working with the language and explain them. Easy to read and allows you to significantly improve your work with the language. Also it can be found in Russian.
Thirdly, the official online documentation. It is made conveniently and allows you to quickly find the necessary sections / methods, with a detailed description and examples.
That's all.
List of materials:
1.
Elixir Tutorial2. Senloren S., Eisenberg D. Introduction to Elixir. Introduction to functional programming. - O'Reilly, 2017.
3.
Official documentation.