Elixir – Useful Resources ”; Previous Next The following resources contain additional information on Elixir. Please use them to get more in-depth knowledge on this topic. Useful Video Courses Master ELIXIR Programming from ZERO to HERO 36 Lectures 3 hours Pranjal Srivastava More Detail Full Stack Web Development – HTML, CSS, JavaScript, PHP, ELIXIR 55 Lectures 6 hours Pranjal Srivastava, Harshit Srivastava More Detail Python and Elixir Programming Bundle Course 81 Lectures 9.5 hours Pranjal Srivastava More Detail Elixir and Phoenix: Real World Functional Programming Course 44 Lectures 4 hours Mohammad Nauman More Detail Elixir Programming Course for Beginners 26 Lectures 2.5 hours Szabo Daniel Erno More Detail Print Page Previous Next Advertisements ”;
Category: elixir
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 – Decision Making
Elixir – Decision Making ”; Previous Next Decision making structures require that the programmer specifies one or more conditions to be evaluated or tested by the program, along with a statement or statements to be executed if the condition is determined to be true, and optionally, other statements to be executed if the condition is determined to be false. Following is the general from of a typical decision making structure found in most of the programming language − Elixir provides if/else conditional constructs like many other programming languages. It also has a cond statement which calls the first true value it finds. Case is another control flow statement which uses pattern matching to control the flow of the program. Let”s have a deep look at them. Elixir provides the following types of decision making statements. Click the following links to check their detail. Sr.No. Statement & Description 1 if statement An if statement consists of a Boolean expression followed by do, one or more executable statements and finally an end keyword. Code in if statement executes only if Boolean condition evaluates to true. 2 if..else statement An if statement can be followed by an optional else statement(within the do..end block), which executes when the Boolean expression is false. 3 unless statement An unless statement has the same body as an if statement. The code within unless statement executes only when the condition specified is false. 4 unless..else statement An unless..else statement has the same body as an if..else statement. The code within unless statement executes only when the condition specified is false. 5 cond A cond statement is used where we want to execute code on basis of several conditions. It kind of works like an if…else if….else construct in several other programming languages. 6 case Case statement can be considered as a replacement for switch statement in imperative languages. Case takes a variable/literal and applies pattern matching to it with different cases. If any case matches, Elixir executes code associated with that case and exits case statement. Print Page Previous Next Advertisements ”;
Elixir – Enumerables
Elixir – Enumerables ”; Previous Next An enumerable is an object that may be enumerated. “Enumerated” means to count off the members of a set/collection/category one by one (usually in order, usually by name). Elixir provides the concept of enumerables and the Enum module to work with them. The functions in the Enum module are limited to, as the name says, enumerating values in data structures. Example of an enumerable data structure is a list, tuple, map, etc. The Enum module provides us with a little over 100 functions to deal with enums. We will discuss a few important functions in this chapter. All of these functions take an enumerable as the first element and a function as the second and work on them. The functions are described below. all? When we use all? function, the entire collection must evaluate to true otherwise false will be returned. For example, to check if all of the elements in the list are odd numbers, then. Live Demo res = Enum.all?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end) IO.puts(res) When the above program is run, it produces the following result − false This is because not all elements of this list are odd. any? As the name suggests, this function returns true if any element of the collection evaluates to true. For example − Live Demo res = Enum.any?([1, 2, 3, 4], fn(s) -> rem(s,2) == 1 end) IO.puts(res) When the above program is run, it produces the following result − true chunk This function divides our collection into small chunks of the size provided as the second argument. For example − res = Enum.chunk([1, 2, 3, 4, 5, 6], 2) IO.puts(res) When the above program is run, it produces the following result − [[1, 2], [3, 4], [5, 6]] each It may be necessary to iterate over a collection without producing a new value, for this case we use the each function − Live Demo Enum.each([“Hello”, “Every”, “one”], fn(s) -> IO.puts(s) end) When the above program is run, it produces the following result − Hello Every one map To apply our function to each item and produce a new collection we use the map function. It is one of the most useful constructs in functional programming as it is quite expressive and short. Let us consider an example to understand this. We will double the values stored in a list and store it in a new list res − res = Enum.map([2, 5, 3, 6], fn(a) -> a*2 end) IO.puts(res) When the above program is run, it produces the following result − [4, 10, 6, 12] reduce The reduce function helps us reduce our enumerable to a single value. To do this, we supply an optional accumulator (5 in this example) to be passed into our function; if no accumulator is provided, the first value is used − Live Demo res = Enum.reduce([1, 2, 3, 4], 5, fn(x, accum) -> x + accum end) IO.puts(res) When the above program is run, it produces the following result − 15 The accumulator is the initial value passed to the fn. From the second call onwards the value returned from previous call is passed as accum. We can also use reduce without the accumulator − Live Demo res = Enum.reduce([1, 2, 3, 4], fn(x, accum) -> x + accum end) IO.puts(res) When the above program is run, it produces the following result − 10 uniq The uniq function removes duplicates from our collection and returns only the set of elements in the collection. For example − res = Enum.uniq([1, 2, 2, 3, 3, 3, 4, 4, 4, 4]) IO.puts(res) When running above program, it produces the following result − [1, 2, 3, 4] Eager Evaluation All the functions in the Enum module are eager. Many functions expect an enumerable and return a list back. This means that when performing multiple operations with Enum, each operation is going to generate an intermediate list until we reach the result. Let us consider the following example to understand this − odd? = &(odd? = &(rem(&1, 2) != 0) res = 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum IO.puts(res) When the above program is run, it produces the following result − 7500000000 The example above has a pipeline of operations. We start with a range and then multiply each element in the range by 3. This first operation will now create and return a list with 100_000 items. Then we keep all odd elements from the list, generating a new list, now with 50_000 items, and then we sum all entries. The |> symbol used in the snippet above is the pipe operator: it simply takes the output from the expression on its left side and passes it as the first argument to the function call on its right side. It’s similar to the Unix | operator. Its purpose is to highlight the flow of data being transformed by a series of functions. Without the pipe operator, the code looks complicated − Enum.sum(Enum.filter(Enum.map(1..100_000, &(&1 * 3)), odd?)) We have many other functions, however, only a few important ones have been described here. Print Page Previous Next Advertisements ”;
Elixir – File I/O
Elixir – File IO ”; Previous Next File IO is an integral part of any programming language as it allows the language to interact with the files on the file system. In this chapter, we will discuss two modules − Path and File. The Path Module The path module is a very small module that can be considered as a helper module for filesystem operations. The majority of the functions in the File module expect paths as arguments. Most commonly, those paths will be regular binaries. The Path module provides facilities for working with such paths. Using functions from the Path module as opposed to just manipulating binaries is preferred since the Path module takes care of different operating systems transparently. It is to be observed that Elixir will automatically convert slashes (/) into backslashes () on Windows when performing file operations. Let us consider the following example to further understand the Path module − Live Demo IO.puts(Path.join(“foo”, “bar”)) When the above program is run, it produces the following result − foo/bar There are a lot of methods that the path module provides. You can have a look at the different methods here. These methods are frequently used if you are performing many file manipulation operations. The File Module The file module contains functions that allow us to open files as IO devices. By default, files are opened in binary mode, which requires developers to use the specific IO.binread and IO.binwrite functions from the IO module. Let us create a file called newfile and write some data to it. {:ok, file} = File.read(“newfile”, [:write]) # Pattern matching to store returned stream IO.binwrite(file, “This will be written to the file”) If you go to open the file we just wrote into, content will be displayed in the following way − This will be written to the file Let us now understand how to use the file module. Opening a file To open a file, we can use any one of the following 2 functions − {:ok, file} = File.open(“newfile”) file = File.open!(“newfile”) Let us now understand the difference between the File.open function and the File.open!() function. The File.open function always returns a tuple. If file is successfully opened, it returns the first value in the tuple as :ok and the second value is literal of type io_device. If an error is caused, it will return a tuple with first value as :error and second value as the reason. The File.open!() function on the other hand will return a io_device if file is successfully opened else it will raise an error. NOTE: This is the pattern followed in all of the file module functions we are going to discuss. We can also specify the modes in which we want to open this file. To open a file as read only and in utf-8 encoding mode, we use the following code − file = File.open!(“newfile”, [:read, :utf8]) Writing to a File We have two ways to write to files. Let us see the first one using the write function from the File module. File.write(“newfile”, “Hello”) But this should not be used if you are making multiple writes to the same file. Every time this function is invoked, a file descriptor is opened and a new process is spawned to write to the file. If you are doing multiple writes in a loop, open the file via File.open and write to it using the methods in IO module. Let us consider an example to understand the same − #Open the file in read, write and utf8 modes. file = File.open!(“newfile_2”, [:read, :utf8, :write]) #Write to this “io_device” using standard IO functions IO.puts(file, “Random text”) You can use other IO module methods like IO.write and IO.binwrite to write to files opened as io_device. Reading from a File We have two ways to read from files. Let us see the first one using the read function from the File module. IO.puts(File.read(“newfile”)) When running this code, you should get a tuple with the first element as :ok and the second one as the contents of newfile We can also use the File.read! function to just get the contents of the files returned to us. Closing an Open File Whenever you open a file using the File.open function, after you are done using it, you should close it using the File.close function − File.close(file) Print Page Previous Next Advertisements ”;
Elixir – Pattern Matching
Elixir – Pattern Matching ”; Previous Next Pattern matching is a technique which Elixir inherits form Erlang. It is a very powerful technique that allows us to extract simpler substructures from complicated data structures like lists, tuples, maps, etc. A match has 2 main parts, a left and a right side. The right side is a data structure of any kind. The left side attempts to match the data structure on the right side and bind any variables on the left to the respective substructure on the right. If a match is not found, the operator raises an error. The simplest match is a lone variable on the left and any data structure on the right. This variable will match anything. For example, Live Demo x = 12 x = “Hello” IO.puts(x) You can place variables inside a structure so that you can capture a substructure. For example, [var_1, _unused_var, var_2] = [{“First variable”}, 25, “Second variable” ] IO.puts(var_1) IO.puts(var_2) This will store the values, {“First variable”} in var_1 and “Second variable” in var_2. There is also a special _ variable(or variables prefixed with ”_”) that works exactly like other variables but tells elixir, “Make sure something is here, but I don”t care exactly what it is.”. In the previous example, _unused_var was one such variable. We can match more complicated patterns using this technique. For example if you want to unwrap and get a number in a tuple which is inside a list which itself is in a list, you can use the following command − Live Demo [_, [_, {a}]] = [“Random string”, [:an_atom, {24}]] IO.puts(a) The above program generates the following result − 24 This will bind a to 24. Other values are ignored as we are using ”_”. In pattern matching, if we use a variable on the right, its value is used. If you want to use the value of a variable on the left, you”ll need to use the pin operator. For example, if you have a variable “a” having value 25 and you want to match it with another variable “b” having value 25, then you need to enter − a = 25 b = 25 ^a = b The last line matches the current value of a, instead of assigning it, to the value of b. If we have a non-matching set of left and right hand side, the match operator raises an error. For example, if we try to match a tuple with a list or a list of size 2 with a list of size 3, an error will be displayed. Print Page Previous Next Advertisements ”;
Elixir – Maps
Elixir – Maps ”; Previous Next Keyword lists are a convenient way to address content stored in lists by key, but underneath, Elixir is still walking through the list. That might be suitable if you have other plans for that list requiring walking through all of it, but it can be an unnecessary overhead if you are planning to use keys as your only approach to the data. This is where maps come to your rescue. Whenever you need a key-value store, maps are the “go to” data structure in Elixir. Creating a Map A map is created using the %{} syntax − map = %{:a => 1, 2 => :b} Compared to the keyword lists, we can already see two differences − Maps allow any value as a key. Maps’ keys do not follow any ordering. Accessing a key In order to acces value associated with a key, Maps use the same syntax as Keyword lists − Live Demo map = %{:a => 1, 2 => :b} IO.puts(map[:a]) IO.puts(map[2]) When the above program is run, it generates the following result − 1 b Inserting a key To insert a key in a map, we use the Dict.put_new function which takes the map, new key and new value as arguments − Live Demo map = %{:a => 1, 2 => :b} new_map = Dict.put_new(map, :new_val, “value”) IO.puts(new_map[:new_val]) This will insert the key-value pair :new_val – “value” in a new map. When the above program is run, it generates the following result − “value” Updating a Value To update a value already present in the map, you can use the following syntax − Live Demo map = %{:a => 1, 2 => :b} new_map = %{ map | a: 25} IO.puts(new_map[:a]) When the above program is run, it generates the following result − 25 Pattern Matching In contrast to keyword lists, maps are very useful with pattern matching. When a map is used in a pattern, it will always match on a subset of the given value − Live Demo %{:a => a} = %{:a => 1, 2 => :b} IO.puts(a) The above program generates the following result − 1 This will match a with 1. And hence, it will generate the output as 1. As shown above, a map matches as long as the keys in the pattern exist in the given map. Therefore, an empty map matches all maps. Variables can be used when accessing, matching and adding map keys − n = 1 map = %{n => :one} %{^n => :one} = %{1 => :one, 2 => :two, 3 => :three} The Map module provides a very similar API to the Keyword module with convenience functions to manipulate maps. You can use functions such as the Map.get, Map.delete, to manipulate maps. Maps with Atom keys Maps come with a few interesting properties. When all the keys in a map are atoms, you can use the keyword syntax for convenience − map = %{:a => 1, 2 => :b} IO.puts(map.a) Another interesting property of maps is that they provide their own syntax for updating and accessing atom keys − Live Demo map = %{:a => 1, 2 => :b} IO.puts(map.a) The above program generates the following result − 1 Note that to access atom keys in this way, it should exist or the program will fail to work. Print Page Previous Next Advertisements ”;
Elixir – Strings
Elixir – Strings ”; Previous Next Strings in Elixir are inserted between double quotes, and they are encoded in UTF-8. Unlike C and C++ where the default strings are ASCII encoded and only 256 different characters are possible, UTF-8 consists of 1,112,064 code points. This means that UTF-8 encoding consists of those many different possible characters. Since the strings use utf-8, we can also use symbols like: ö, ł, etc. Create a String To create a string variable, simply assign a string to a variable − str = “Hello world” To print this to your console, simply call the IO.puts function and pass it the variable str − Live Demo str = str = “Hello world” IO.puts(str) The above program generates the following result − Hello World Empty Strings You can create an empty string using the string literal, “”. For example, Live Demo a = “” if String.length(a) === 0 do IO.puts(“a is an empty string”) end The above program generates the following result. a is an empty string String Interpolation String interpolation is a way to construct a new String value from a mix of constants, variables, literals, and expressions by including their values inside a string literal. Elixir supports string interpolation, to use a variable in a string, when writing it, wrap it with curly braces and prepend the curly braces with a ”#” sign. For example, Live Demo x = “Apocalypse” y = “X-men #{x}” IO.puts(y) This will take the value of x and substitute it in y. The above code will generate the following result − X-men Apocalypse String Concatenation We have already seen the use of String concatenation in previous chapters. The ”<>” operator is used to concatenate strings in Elixir. To concatenate 2 strings, Live Demo x = “Dark” y = “Knight” z = x <> ” ” <> y IO.puts(z) The above code generates the following result − Dark Knight String Length To get the length of the string, we use the String.length function. Pass the string as a parameter and it will show you its size. For example, Live Demo IO.puts(String.length(“Hello”)) When running above program, it produces following result − 5 Reversing a String To reverse a string, pass it to the String.reverse function. For example, Live Demo IO.puts(String.reverse(“Elixir”)) The above program generates the following result − rixilE String Comparison To compare 2 strings, we can use the == or the === operators. For example, Live Demo var_1 = “Hello world” var_2 = “Hello Elixir” if var_1 === var_2 do IO.puts(“#{var_1} and #{var_2} are the same”) else IO.puts(“#{var_1} and #{var_2} are not the same”) end The above program generates the following result − Hello world and Hello elixir are not the same. String Matching We have already seen the use of the =~ string match operator. To check if a string matches a regex, we can also use the string match operator or the String.match? function. For example, Live Demo IO.puts(String.match?(“foo”, ~r/foo/)) IO.puts(String.match?(“bar”, ~r/foo/)) The above program generates the following result − true false This same can also be achieved by using the =~ operator. For example, Live Demo IO.puts(“foo” =~ ~r/foo/) The above program generates the following result − true String Functions Elixir supports a large number of functions related to strings, some of the most used are listed in the following table. Sr.No. Function and its Purpose 1 at(string, position) Returns the grapheme at the position of the given utf8 string. If position is greater than string length, then it returns nil 2 capitalize(string) Converts the first character in the given string to uppercase and the remainder to lowercase 3 contains?(string, contents) Checks if string contains any of the given contents 4 downcase(string) Converts all characters in the given string to lowercase 5 ends_with?(string, suffixes) Returns true if string ends with any of the suffixes given 6 first(string) Returns the first grapheme from a utf8 string, nil if the string is empty 7 last(string) Returns the last grapheme from a utf8 string, nil if the string is empty 8 replace(subject, pattern, replacement, options \ []) Returns a new string created by replacing occurrences of pattern in subject with replacement 9 slice(string, start, len) Returns a substring starting at the offset start, and of length len 10 split(string) Divides a string into substrings at each Unicode whitespace occurrence with leading and trailing whitespace ignored. Groups of whitespace are treated as a single occurrence. Divisions do not occur on non-breaking whitespace 11 upcase(string) Converts all characters in the given string to uppercase Binaries A binary is just a sequence of bytes. Binaries are defined using << >>. For example: << 0, 1, 2, 3 >> Of course, those bytes can be organized in any way, even in a sequence that does not make them a valid string. For example, << 239, 191, 191 >> Strings are also binaries. And the string concatenation operator <> is actually a Binary concatenation operator: IO.puts(<< 0, 1 >> <> << 2, 3 >>) The above code generates the following result − << 0, 1, 2, 3 >> Note the ł character. Since this is utf-8 encoded, this character representation takes up 2 bytes. Since each number represented in a binary is meant to be a byte, when this value goes up from 255, it is truncated. To prevent this, we use size modifier to specify how many bits we want that number to take. For example − IO.puts(<< 256 >>) # truncated, it”ll print << 0 >> IO.puts(<< 256 :: size(16) >>) #Takes 16 bits/2 bytes, will print << 1, 0 >> The above program will generate the following result − << 0 >> << 1, 0 >> We can also use the utf8 modifier, if a character is code point then, it will be produced in the output; else the bytes − Live Demo IO.puts(<< 256 :: utf8 >>) The above program generates the following result − Ā We also have a function called is_binary that checks if a given