Elixir – Macros

Elixir – Macros ”; Previous Next Macros are one of the most advanced and powerful features of Elixir. As with all advanced features of any language, macros should be used sparingly. They make it possible to perform powerful code transformations in compilation time. We will now understand what macros are and how to use them in brief. Quote Before we start talking about macros, let us first look at Elixir internals. An Elixir program can be represented by its own data structures. The building block of an Elixir program is a tuple with three elements. For example, the function call sum(1, 2, 3) is represented internally as − {:sum, [], [1, 2, 3]} The first element is the function name, the second is a keyword list containing metadata and the third is the arguments list. You can get this as the output in iex shell if you write the following − quote do: sum(1, 2, 3) Operators are also represented as such tuples. Variables are also represented using such triplets, except that the last element is an atom, instead of a list. When quoting more complex expressions, we can see that the code is represented in such tuples, which are often nested inside each other in a structure resembling a tree. Many languages would call such representations an Abstract Syntax Tree (AST). Elixir calls these quoted expressions. Unquote Now that we can retrieve the internal structure of our code, how do we modify it? To inject new code or values, we use unquote. When we unquote an expression it will be evaluated and injected into the AST. Let us consider an example(in iex shell) to understand the concept − num = 25 quote do: sum(15, num) quote do: sum(15, unquote(num)) When the above program is run, it produces the following result − {:sum, [], [15, {:num, [], Elixir}]} {:sum, [], [15, 25]} In the example for the quote expression, it did not automatically replace num with 25. We need to unquote this variable if we want to modify the AST. Macros So now that we are familiar with quote and unquote, we can explore metaprogramming in Elixir using macros. In the simplest of terms macros are special functions designed to return a quoted expression that will be inserted into our application code. Imagine the macro being replaced with the quoted expression rather than called like a function. With macros we have everything necessary to extend Elixir and dynamically add code to our applications Let us implement unless as a macro. We will begin by defining the macro using the defmacro macro. Remember that our macro needs to return a quoted expression. defmodule OurMacro do defmacro unless(expr, do: block) do quote do if !unquote(expr), do: unquote(block) end end end require OurMacro OurMacro.unless true, do: IO.puts “True Expression” OurMacro.unless false, do: IO.puts “False expression” When the above program is run, it produces the following result − False expression What is happening here is our code is being replaced by the quoted code returned by the unless macro. We have unquoted the expression to evaluate it in current context and also unquoted the do block to execute it in its context. This example shows us metaprogramming using macros in elixir. Macros can be used in much more complex tasks but should be used sparingly. This is because metaprogramming in general is considered a bad practice and should be used only when necessary. Print Page Previous Next Advertisements ”;

Elixir – Libraries

Elixir – Libraries ”; Previous Next Elixir provides excellent interoperability with Erlang libraries. Let us discuss a few libraries in brief. The Binary Module The built-in Elixir String module handles binaries that are UTF-8 encoded. The binary module is useful when you are dealing with binary data that is not necessarily UTF-8 encoded. Let us consider an example to further understand the Binary module − # UTF-8 IO.puts(String.to_char_list(“Ø”)) # binary IO.puts(:binary.bin_to_list “Ø”) When the above program is run, it produces the following result − [216] [195, 152] The above example shows the difference; the String module returns UTF-8 codepoints, while :binary deals with raw data bytes. The Crypto Module The crypto module contains hashing functions, digital signatures, encryption and more. This module is not part of the Erlang standard library, but is included with the Erlang distribution. This means you must list :crypto in your project’s applications list whenever you use it. Let us see an example using the crypto module − Live Demo IO.puts(Base.encode16(:crypto.hash(:sha256, “Elixir”))) When the above program is run, it produces the following result − 3315715A7A3AD57428298676C5AE465DADA38D951BDFAC9348A8A31E9C7401CB The Digraph Module The digraph module contains functions for dealing with directed graphs built of vertices and edges. After constructing the graph, the algorithms in there will help finding, for instance, the shortest path between two vertices, or loops in the graph. Note that the functions in :digraph alter the graph structure indirectly as a side effect, while returning the added vertices or edges. Live Demo digraph = :digraph.new() coords = [{0.0, 0.0}, {1.0, 0.0}, {1.0, 1.0}] [v0, v1, v2] = (for c <- coords, do: :digraph.add_vertex(digraph, c)) :digraph.add_edge(digraph, v0, v1) :digraph.add_edge(digraph, v1, v2) for point <- :digraph.get_short_path(digraph, v0, v2) do {x, y} = point IO.puts(“#{x}, #{y}”) end When the above program is run, it produces the following result − 0.0, 0.0 1.0, 0.0 1.0, 1.0 The Math Module The math module contains common mathematical operations covering trigonometry, exponential and logarithmic functions. Let us consider the following example to understand how the Math module works − Live Demo # Value of pi IO.puts(:math.pi()) # Logarithm IO.puts(:math.log(7.694785265142018e23)) # Exponentiation IO.puts(:math.exp(55.0)) #… When the above program is run, it produces the following result − 3.141592653589793 55.0 7.694785265142018e23 The Queue Module The queue is a data structure that implements (double-ended) FIFO (first-in first-out) queues efficiently. The following example shows how a Queue module works − Live Demo q = :queue.new q = :queue.in(“A”, q) q = :queue.in(“B”, q) {{:value, val}, q} = :queue.out(q) IO.puts(val) {{:value, val}, q} = :queue.out(q) IO.puts(val) When the above program is run, it produces the following result − A B Print Page Previous Next Advertisements ”;

Elixir – Typespecs

Elixir – Typespecs ”; Previous Next Elixir is a dynamically typed language, so all types in Elixir are inferred by the runtime. Nonetheless, Elixir comes with typespecs, which are a notation used for declaring custom data types and declaring typed function signatures (specifications). Function Specifications(specs) By default, Elixir provides some basic types, such as integer or pid, and also complex types: for example, the round function, which rounds a float to its nearest integer, takes a number as an argument (an integer or a float) and returns an integer. In the related documentation, the round typed signature is written as − round(number) :: integer The above description implies that the function on the left takes as argument what is specified in parenthesis and returns what is on the right of ::, i.e., Integer. Function specs are written with the @spec directive, placed right before the function definition. The round function can be written as − @spec round(number) :: integer def round(number), do: # Function implementation … Typespecs support complex types as well, for example, if you want to return a list of integers, then you can use [Integer] Custom Types While Elixir provides a lot of useful inbuilt types, it is convenient to define custom types when appropriate. This can be done when defining modules through the @type directive. Let us consider an example to understand the same − Live Demo defmodule FunnyCalculator do @type number_with_joke :: {number, String.t} @spec add(number, number) :: number_with_joke def add(x, y), do: {x + y, “You need a calculator to do that?”} @spec multiply(number, number) :: number_with_joke def multiply(x, y), do: {x * y, “It is like addition on steroids.”} end {result, comment} = FunnyCalculator.add(10, 20) IO.puts(result) IO.puts(comment) When the above program is run, it produces the following result − 30 You need a calculator to do that? NOTE − Custom types defined through @type are exported and available outside the module they are defined in. If you want to keep a custom type private, you can use the @typep directive instead of @type. Print Page Previous Next Advertisements ”;

Elixir – Aliases

Elixir – Aliases ”; Previous Next In order to facilitate software reuse, Elixir provides three directives – alias, require and import. It also provides a macro called use which is summarized below − # Alias the module so it can be called as Bar instead of Foo.Bar alias Foo.Bar, as: Bar # Ensure the module is compiled and available (usually for macros) require Foo # Import functions from Foo so they can be called without the `Foo.` prefix import Foo # Invokes the custom code defined in Foo as an extension point use Foo Let us now understand in detail about each directive. alias The alias directive allows you to set up aliases for any given module name. For example, if you want to give an alias ”Str” to the String module, you can simply write − Live Demo alias String, as: Str IO.puts(Str.length(“Hello”)) The above program generates the following result − 5 An alias is given to the String module as Str. Now when we call any function using the Str literal, it actually references to the String module. This is very helpful when we use very long module names and want to substitute those with shorter ones in the current scope. NOTE − Aliases MUST start with a capital letter. Aliases are valid only within the lexical scope they are called in. For example, if you have 2 modules in a file and make an alias within one of the modules, that alias will not be accessible in the second module. If you give the name of an in built module, like String or Tuple, as an alias to some other module, to access the inbuilt module, you will need to prepend it with “Elixir.”. For example, Live Demo alias List, as: String #Now when we use String we are actually using List. #To use the string module: IO.puts(Elixir.String.length(“Hello”)) When the above program is run, it generates the following result − 5 require Elixir provides macros as a mechanism for meta-programming (writing code that generates code). Macros are chunks of code that are executed and expanded at compilation time. This means, in order to use a macro, we need to guarantee that its module and implementation are available during compilation. This is done with the require directive. Integer.is_odd(3) When the above program is run, it will generate the following result − ** (CompileError) iex:1: you must require Integer before invoking the macro Integer.is_odd/1 In Elixir, Integer.is_odd is defined as a macro. This macro can be used as a guard. This means that, in order to invoke Integer.is_odd, we will need the Integer module. Use the require Integer function and run the program as shown below. require Integer Integer.is_odd(3) This time the program will run and produce the output as: true. In general, a module is not required before usage, except if we want to use the macros available in that module. An attempt to call a macro that was not loaded will raise an error. Note that like the alias directive, require is also lexically scoped. We will talk more about macros in a later chapter. import We use the import directive to easily access functions or macros from other modules without using the fully-qualified name. For instance, if we want to use the duplicate function from the List module several times, we can simply import it. import List, only: [duplicate: 2] In this case, we are importing only the function duplicate (with argument list length 2) from List. Although :only is optional, its usage is recommended in order to avoid importing all the functions of a given module inside the namespace. :except could also be given as an option in order to import everything in a module except a list of functions. The import directive also supports :macros and :functions to be given to :only. For example, to import all macros, a user can write − import Integer, only: :macros Note that import too is Lexically scoped just like the require and the alias directives. Also note that ”import”ing a module also ”require”s it. use Although not a directive, use is a macro tightly related to require that allows you to use a module in the current context. The use macro is frequently used by developers to bring external functionality into the current lexical scope, often modules. Let us understand the use directive through an example − defmodule Example do use Feature, option: :value end Use is a macro that transforms the above into − defmodule Example do require Feature Feature.__using__(option: :value) end The use Module first requires the module and then calls the __using__ macro on Module. Elixir has great metaprogramming capabilities and it has macros to generate code at compile time. The __using__ macro is called in the above instance, and the code is injected into our local context. The local context is where the use macro was called at the time of compilation. Print Page Previous Next Advertisements ”;

Elixir – Loops

Elixir – Loops ”; Previous Next Due to immutability, loops in Elixir (as in any functional programming language) are written differently from imperative languages. For example, in an imperative language like C, you will write − for(i = 0; i < 10; i++) { printf(“%d”, array[i]); } In the example given above, we are mutating both the array and the variable i. Mutating is not possible in Elixir. Instead, functional languages rely on recursion: a function is called recursively until a condition is reached that stops the recursive action from continuing. No data is mutated in this process. Let us now write a simple loop using recursion that prints hello n times. Live Demo defmodule Loop do def print_multiple_times(msg, n) when n <= 1 do IO.puts msg end def print_multiple_times(msg, n) do IO.puts msg print_multiple_times(msg, n – 1) end end Loop.print_multiple_times(“Hello”, 10) When the above program is run, it produces the following result − Hello Hello Hello Hello Hello Hello Hello Hello Hello Hello We have utilized function”s pattern matching techniques and recursion to successfully implement a loop. Recursive definitions are difficult to understand but converting loops to recursion is easy. Elixir provides us the Enum module. This module is used for the most iterative looping calls as it is much easier to use those than trying to figure out recursive definitions for the same. We will discuss those in the next chapter. Your own recursive definitions should only be used when you dont find a solution using that module. Those functions are tail call optimized and quite fast. Print Page Previous Next Advertisements ”;

Elixir – Protocols

Elixir – Protocols ”; Previous Next Protocols are a mechanism to achieve polymorphism in Elixir. Dispatching on a protocol is available to any data type as long as it implements the protocol. Let us consider an example of using protocols. We used a function called to_string in the previous chapters to convert from other types to the string type. This is actually a protocol. It acts according to the input that is given without producing an error. This might seem like we are discussing pattern matching functions, but as we proceed further, it turns out different. Consider the following example to further understand the protocol mechanism. Let us create a protocol that will display if the given input is empty or not. We will call this protocol blank?. Defining a Protocol We can define a protocol in Elixir in the following way − defprotocol Blank do def blank?(data) end As you can see, we do not need to define a body for the function. If you are familiar with interfaces in other programming languages, you can think of a Protocol as essentially the same thing. So this Protocol is saying that anything that implements it must have an empty? function, although it is up to the implementor as to how the function responds. With the protocol defined, let us understand how to add a couple of implementations. Implementing a Protocol Since we have defined a protocol, we now need to tell it how to handle the different inputs that it might get. Let us build on the example we had taken earlier. We will implement the blank protocol for lists, maps and strings. This will show if the thing we passed is blank or not. Live Demo #Defining the protocol defprotocol Blank do def blank?(data) end #Implementing the protocol for lists defimpl Blank, for: List do def blank?([]), do: true def blank?(_), do: false end #Implementing the protocol for strings defimpl Blank, for: BitString do def blank?(“”), do: true def blank?(_), do: false end #Implementing the protocol for maps defimpl Blank, for: Map do def blank?(map), do: map_size(map) == 0 end IO.puts(Blank.blank? []) IO.puts(Blank.blank? [:true, “Hello”]) IO.puts(Blank.blank? “”) IO.puts(Blank.blank? “Hi”) You can implement your Protocol for as many or as few types as you want, whatever makes sense for the usage of your Protocol. This was a pretty basic use case of protocols. When the above program is run, it produces the following result − true false true false Note − If you use this for any types other than those you defined the protocol for, it will produce an error. Print Page Previous Next Advertisements ”;

Elixir – Structs

Elixir – Structs ”; Previous Next Structs are extensions built on top of maps that provide compile-time checks and default values. Defining Structs To define a struct, the defstruct construct is used − defmodule User do defstruct name: “John”, age: 27 end The keyword list used with defstruct defines what fields the struct will have along with their default values. Structs take the name of the module they are defined in. In the example given above, we defined a struct named User. We can now create User structs by using a syntax similar to the one used to create maps − new_john = %User{}) ayush = %User{name: “Ayush”, age: 20} megan = %User{name: “Megan”}) The above code will generate three different structs with values − %User{age: 27, name: “John”} %User{age: 20, name: “Ayush”} %User{age: 27, name: “Megan”} Structs provide compile-time guarantees that only the fields (and all of them) defined through defstruct will be allowed to exist in a struct. So you cannot define your own fields once you have created the struct in the module. Accessing and Updating Structs When we discussed maps, we showed how we can access and update the fields of a map. The same techniques (and the same syntax) apply to structs as well. For example, if we want to update the user we created in the earlier example, then − defmodule User do defstruct name: “John”, age: 27 end john = %User{} #john right now is: %User{age: 27, name: “John”} #To access name and age of John, IO.puts(john.name) IO.puts(john.age) When the above program is run, it produces the following result − John 27 To update a value in a struct, we will again use the same procedure that we used in the map chapter, meg = %{john | name: “Meg”} Structs can also be used in pattern matching, both for matching on the value of specific keys as well as for ensuring that the matching value is a struct of the same type as the matched value. Print Page Previous Next Advertisements ”;

Elixir – Streams

Elixir – Streams ”; Previous Next Many functions expect an enumerable and return a list back. It means, while performing multiple operations with Enum, each operation is going to generate an intermediate list until we reach the result. Streams support lazy operations as opposed to eager operations by enums. In short, streams are lazy, composable enumerables. What this means is Streams do not perform an operation unless it is absolutely needed. Let us consider an example to understand this − Live Demo odd? = &(rem(&1, 2) != 0) res = 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(odd?) |> Enum.sum IO.puts(res) When the above program is run, it produces the following result − 7500000000 In the example given above, 1..100_000 |> Stream.map(&(&1 * 3)) returns a data type, an actual stream, that represents the map computation over the range 1..100_000. It has not yet evaluated this representation. Instead of generating intermediate lists, streams build a series of computations that are invoked only when we pass the underlying stream to the Enum module. Streams are useful when working with large, possibly infinite, collections. Streams and enums have many functions in common. Streams mainly provide the same functions provided by the Enum module which generated Lists as their return values after performing computations on input enumerables. Some of them are listed in the following table − Sr.No. Function and its Description 1 chunk(enum, n, step, leftover \ nil) Streams the enumerable in chunks, containing n items each, where each new chunk starts step elements into the enumerable. 2 concat(enumerables) Creates a stream that enumerates each enumerable in an enumerable. 3 each(enum, fun) Executes the given function for each item. 4 filter(enum, fun) Creates a stream that filters elements according to the given function on enumeration. 5 map(enum, fun) Creates a stream that will apply the given function on enumeration. 6 drop(enum, n) Lazily drops the next n items from the enumerable. Print Page Previous Next Advertisements ”;

Elixir – Comprehensions

Elixir – Comprehensions ”; Previous Next List comprehensions are syntactic sugar for looping through enumerables in Elixir. In this chapter we will use comprehensions for iteration and generation. Basics When we looked at the Enum module in the enumerables chapter, we came across the map function. Enum.map(1..3, &(&1 * 2)) In this example, we will pass a function as the second argument. Each item in the range will be passed into the function, and then a new list will be returned containing the new values. Mapping, filtering, and transforming are very common actions in Elixir and so there is a slightly different way of achieving the same result as the previous example − for n <- 1..3, do: n * 2 When we run the above code, it produces the following result − [2, 4, 6] The second example is a comprehension, and as you can probably see, it is simply syntactic sugar for what you can also achieve if you use the Enum.map function. However, there are no real benefits to using a comprehension over a function from the Enum module in terms of performance. Comprehensions are not limited to lists but can be used with all enumerables. Filter You can think of filters as a sort of guard for comprehensions. When a filtered value returns false or nil it is excluded from the final list. Let us loop over a range and only worry about even numbers. We will use the is_even function from the Integer module to check if a value is even or not. import Integer IO.puts(for x <- 1..10, is_even(x), do: x) When the above code is run, it produces the following result − [2, 4, 6, 8, 10] We can also use multiple filters in the same comprehension. Add another filter that you want after the is_even filter separated by a comma. :into Option In the examples above, all the comprehensions returned lists as their result. However, the result of a comprehension can be inserted into different data structures by passing the :into option to the comprehension. For example, a bitstring generator can be used with the :into option in order to easily remove all spaces in a string − Live Demo IO.puts(for <<c <- ” hello world “>>, c != ?s, into: “”, do: <<c>>) When the above code is run, it produces the following result − helloworld The above code removes all spaces from the string using c != ?s filter and then using the :into option, it puts all the returned characters in a string. Print Page Previous Next Advertisements ”;

Elixir – Quick Guide

Elixir – Quick Guide ”; Previous Next Elixir – Overview Elixir is a dynamic, functional language designed for building scalable and maintainable applications. It leverages the Erlang VM, known for running low-latency, distributed and fault-tolerant systems, while also being successfully used in web development and the embedded software domain. Elixir is a functional, dynamic language built on top of Erlang and the Erlang VM. Erlang is a language that was originally written in 1986 by Ericsson to help solve telephony problems like distribution, fault-tolerance, and concurrency. Elixir, written by José Valim, extends Erlang and provides a friendlier syntax into the Erlang VM. It does this while keeping the performance of the same level as Erlang. Features of Elixir Let us now discuss a few important features of Elixir − Scalability − All Elixir code runs inside lightweight processes that are isolated and exchange information via messages. Fault Tolerance − Elixir provides supervisors which describe how to restart parts of your system when things go wrong, going back to a known initial state that is guaranteed to work. This ensures your application/platform is never down. Functional Programming − Functional programming promotes a coding style that helps developers write code that is short, fast, and maintainable. Build tools − Elixir ships with a set of development tools. Mix is one such tool that makes it easy to create projects, manage tasks, run tests, etc. It also has its own package manager − Hex. Erlang Compatibility − Elixir runs on the Erlang VM giving developers complete access to Erlang’s ecosystem. Elixir – Environment In order to run Elixir, you need to set it up locally on your system. To install Elixir, you will first require Erlang. On some platforms, Elixir packages come with Erlang in them. Installing Elixir Let us now understand the installation of Elixir in different Operating Systems. Windows Setup To install Elixir on windows, download installer from https://elixir-lang.org/install.html#windows and simply click Next to proceed through all steps. You will have it on your local system. If you have any problems while installing it, you can check this page for more info. Mac Setup If you have Homebrew installed, make sure that it is the latest version. For updating, use the following command − brew update Now, install Elixir using the command given below − brew install elixir Ubuntu/Debian Setup The steps to install Elixir in an Ubuntu/Debian setup is as follows − Add Erlang Solutions repo − wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i erlang-solutions_1.0_all.deb sudo apt-get update Install the Erlang/OTP platform and all of its applications − sudo apt-get install esl-erlang Install Elixir − sudo apt-get install elixir Other Linux Distros If you have any other Linux distribution, please visit this page to set up elixir on your local system. Testing the Setup To test the Elixir setup on your system, open your terminal and enter iex in it. It will open the interactive elixir shell like the following − Erlang/OTP 19 [erts-8.0] [source-6dc93c1] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] Interactive Elixir (1.3.1) – press Ctrl+C to exit (type h() ENTER for help) iex(1)> Elixir is now successfully set up on your system. Elixir – Basic Syntax We will start with the customary ”Hello World” program. To start the Elixir interactive shell, enter the following command. iex After the shell starts, use the IO.puts function to “put” the string on the console output. Enter the following in your Elixir shell − Live Demo IO.puts “Hello world” In this tutorial, we will use the Elixir script mode where we will keep the Elixir code in a file with the extension .ex. Let us now keep the above code in the test.ex file. In the succeeding step, we will execute it using elixirc− Live Demo IO.puts “Hello world” Let us now try to run the above program as follows − $elixirc test.ex The above program generates the following result − Hello World Here we are calling a function IO.puts to generate a string to our console as output. This function can also be called the way we do in C, C++, Java, etc., providing arguments in parentheses following the function name − IO.puts(“Hello world”) Comments Single line comments start with a ”#” symbol. There”s no multi-line comment, but you can stack multiple comments. For example − #This is a comment in Elixir Line Endings There are no required line endings like ”;” in Elixir. However, we can have multiple statements in the same line, using ”;”. For example, Live Demo IO.puts(“Hello”); IO.puts(“World!”) The above program generates the following result − Hello World! Identifiers Identifiers like variables, function names are used to identify a variable, function, etc. In Elixir, you can name your identifiers starting with a lower case alphabet with numbers, underscores and upper case letters thereafter. This naming convention is commonly known as snake_case. For example, following are some valid identifiers in Elixir − var1 variable_2 one_M0r3_variable Please note that variables can also be named with a leading underscore. A value that is not meant to be used must be assigned to _ or to a variable starting with underscore − _some_random_value = 42 Also elixir relies on underscores to make functions private to modules. If you name a function with a leading underscore in a module, and import that module, this function will not be imported. There are many more intricacies related to function naming in Elixir which we will discuss in coming chapters. Reserved Words Following words are reserved and cannot be used as variables, module or function names. after and catch do inbits inlist nil else end not or false fn in rescue true when xor __MODULE__ __FILE__ __DIR__ __ENV__ __CALLER__ Elixir – Data Types For using any language, you need to understand the basic data types the language supports. In this chapter, we will discuss 7 basic data types supported by the elixir language: integers, floats, Booleans, atoms, strings, lists and tuples. Numerical Types Elixir, like any other programming language, supports both integers and