Author: Saim Khalid

  • Lists and Tuples

    (Linked) Lists

    A linked list is a heterogeneous list of elements that are stored at different locations in memory and are kept track of by using references. Linked lists are data structures especially used in functional programming.

    Elixir uses square brackets to specify a list of values. Values can be of any type −

    [1, 2, true, 3]

    When Elixir sees a list of printable ASCII numbers, Elixir will print that as a char list (literally a list of characters). Whenever you see a value in IEx and you are not sure what it is, you can use the i function to retrieve information about it.

    Live Demo

    IO.puts([104, 101, 108, 108, 111])

    The above characters in the list are all printable. When the above program is run, it produces the following result −

    hello
    

    You can also define lists the other way round, using single quotes −

    Live Demo

    IO.puts(is_list('Hello'))

    When the above program is run, it produces the following result −

    true
    

    Keep in mind single-quoted and double-quoted representations are not equivalent in Elixir as they are represented by different types.

    Length of a List

    To find the length of a list, we use the length function as in the following program −

    Live Demo

    IO.puts(length([1, 2, :true, "str"]))

    The above program generates the following result −

    4
    

    Concatenation and Subtraction

    Two lists can be concatenated and subtracted using the ++ and  operators. Consider the following example to understand the functions.

    IO.puts([1, 2, 3] ++ [4, 5, 6])
    IO.puts([1, true, 2, false, 3, true] -- [true, false])

    This will give you a concatenated string in the first case and a subtracted string in the second. The above program generates the following result −

    [1, 2, 3, 4, 5, 6]
    [1, 2, 3, true]
    

    Head and Tail of a List

    The head is the first element of a list and the tail is the remainder of a list. They can be retrieved with the functions hd and tl. Let us assign a list to a variable and retrieve its head and tail.

    list = [1, 2, 3]
    IO.puts(hd(list))
    IO.puts(tl(list))

    This will give us the head and tail of the list as output. The above program generates the following result −

    1
    [2, 3]
    

    Note − Getting the head or the tail of an empty list is an error.

    Other List functions

    Elixir standard library provides a whole lot of functions to deal with lists. We will have a look at some of those here.

    S.no.Function Name and Description
    1delete(list, item)Deletes the given item from the list. Returns a list without the item. If the item occurs more than once in the list, just the first occurrence is removed.
    2delete_at(list, index)Produces a new list by removing the value at the specified index. Negative indices indicate an offset from the end of the list. If index is out of bounds, the original list is returned.
    3first(list)Returns the first element in list or nil if list is empty.
    4flatten(list)Flattens the given list of nested lists.
    5insert_at(list, index, value)Returns a list with value inserted at the specified index. Note that index is capped at the list length. Negative indices indicate an offset from the end of the list.
    6last(list)Returns the last element in list or nil if list is empty.

    Tuples

    Tuples are also data structures which store a number of other structures within them. Unlike lists, they store elements in a contiguous block of memory. This means accessing a tuple element per index or getting the tuple size is a fast operation. Indexes start from zero.

    Elixir uses curly brackets to define tuples. Like lists, tuples can hold any value −

    {:ok, "hello"}
    

    Length of a Tuple

    To get the length of a tuple, use the tuple_size function as in the following program −

    Live Demo

    IO.puts(tuple_size({:ok, "hello"}))
    

    The above program generates the following result −

    2
    

    Appending a Value

    To append a value to the tuple, use the Tuple.append function −

    tuple = {:ok, "Hello"}
    Tuple.append(tuple, :world)

    This will create and return a new tuple: {:ok, “Hello”, :world}

    Inserting a Value

    To insert a value at a given position, we can either use the Tuple.insert_at function or the put_elem function. Consider the following example to understand the same −

    tuple = {:bar, :baz}
    new_tuple_1 = Tuple.insert_at(tuple, 0, :foo)
    new_tuple_2 = put_elem(tuple, 1, :foobar)

    Notice that put_elem and insert_at returned new tuples. The original tuple stored in the tuple variable was not modified because Elixir data types are immutable. By being immutable, Elixir code is easier to reason about as you never need to worry if a particular code is mutating your data structure in place.

    Tuples vs. Lists

    What is the difference between lists and tuples?

    Lists are stored in memory as linked lists, meaning that each element in a list holds its value and points to the following element until the end of the list is reached. We call each pair of value and pointer a cons cell. This means accessing the length of a list is a linear operation: we need to traverse the whole list in order to figure out its size. Updating a list is fast as long as we are prepending elements.

    Tuples, on the other hand, are stored contiguously in memory. This means getting the tuple size or accessing an element by index is fast. However, updating or adding elements to tuples is expensive because it requires copying the whole tuple in memory.

  • Char lists

    A char list is nothing more than a list of characters. Consider the following program to understand the same.

    IO.puts('Hello')
    IO.puts(is_list('Hello'))

    The above program generates the following result −

    Hello
    true
    

    Instead of containing bytes, a char list contains the code points of the characters between single-quotes. So while the double-quotes represent a string (i.e. a binary), singlequotes represent a char list (i.e. a list). Note that IEx will generate only code points as output if any of the chars is outside the ASCII range.

    Char lists are used mostly when interfacing with Erlang, in particular old libraries that do not accept binaries as arguments. You can convert a char list to a string and back by using the to_string(char_list) and to_char_list(string) functions −

    IO.puts(is_list(to_char_list("hełło")))
    IO.puts(is_binary(to_string ('hełło')))

    The above program generates the following result −

    true
    true
    

    NOTE − The functions to_string and to_char_list are polymorphic, i.e., they can take multiple types of input like atoms, integers and convert them to strings and char lists respectively.

  • Lists (Basic Operations)

    In this chapter, we will discuss how to carry out some basic operations on Lists, such as −

    Sr.NoBasic Operation & Description
    1Inserting Elements into a ListMutable Lists can grow dynamically at runtime. The List.add() function appends the specified value to the end of the List and returns a modified List object.
    2Updating a listLists in Dart can be updated by −Updating The IndexUsing the List.replaceRange() function
    3Removing List itemsThe following functions supported by the List class in the dart:core library can be used to remove the item(s) in a List.
  • Lists

    A very commonly used collection in programming is an array. Dart represents arrays in the form of List objects. A List is simply an ordered group of objects. The dart:core library provides the List class that enables creation and manipulation of lists.

    The logical representation of a list in Dart is given below −

    Logical Representation of a List
    • test_list − is the identifier that references the collection.
    • The list contains in it the values 12, 13, and 14. The memory blocks holding these values are known as elements.
    • Each element in the List is identified by a unique number called the index. The index starts from zero and extends up to n-1 where n is the total number of elements in the List. The index is also referred to as the subscript.

    Lists can be classified as −

    • Fixed Length List
    • Growable List

    Let us now discuss these two types of lists in detail.

    Fixed Length List

    A fixed length list’s length cannot change at runtime. The syntax for creating a fixed length list is as given below −

    Step 1 − Declaring a list

    The syntax for declaring a fixed length list is given below −

    var list_name = new List(initial_size)
    

    The above syntax creates a list of the specified size. The list cannot grow or shrink at runtime. Any attempt to resize the list will result in an exception.

    Step 2 − Initializing a list

    The syntax for initializing a list is as given below −

    lst_name[index] = value;
    

    Example

    void main() { 
       var lst = new List(3); 
       lst[0] = 12; 
       lst[1] = 13; 
       lst[2] = 11; 
       print(lst); 
    }

    It will produce the following output −

    [12, 13, 11]
    

    Growable List

    A growable list’s length can change at run-time. The syntax for declaring and initializing a growable list is as given below −

    Step 1 − Declaring a List

    var list_name = [val1,val2,val3]   
    --- creates a list containing the specified values  
    OR  
    var list_name = new List() 
    --- creates a list of size zero 
    

    Step 2 − Initializing a List

    The index / subscript is used to reference the element that should be populated with a value. The syntax for initializing a list is as given below −

    list_name[index] = value;
    

    Example

    The following example shows how to create a list of 3 elements.Live Demo

    void main() { 
       var num_list = [1,2,3]; 
       print(num_list); 
    }

    It will produce the following output −

    [1, 2, 3]
    

    Example

    The following example creates a zero-length list using the empty List() constructor. The add() function in the List class is used to dynamically add elements to the list.

    void main() { 
       var lst = new List(); 
       lst.add(12); 
       lst.add(13); 
       print(lst); 
    } 

    It will produce the following output −

    [12, 13] 
    

    List Properties

    The following table lists some commonly used properties of the List class in the dart:core library.

    Sr.NoMethods & Description
    1firstReturns the first element in the list.
    2isEmptyReturns true if the collection has no elements.
    3isNotEmptyReturns true if the collection has at least one element.
    4lengthReturns the size of the list.
    5lastReturns the last element in the list.
    6reversedReturns an iterable object containing the lists values in the reverse order.
    7SingleChecks if the list has only one element and returns it.
  • Boolean

    Dart provides an inbuilt support for the Boolean data type. The Boolean data type in DART supports only two values – true and false. The keyword bool is used to represent a Boolean literal in DART.

    The syntax for declaring a Boolean variable in DART is as given below −

    bool var_name = true;  
    OR  
    bool var_name = false 
    

    Example

    void main() { 
       bool test; 
       test = 12 > 5; 
       print(test); 
    }

    It will produce the following output −

    true 
    

    Example

    Unlike JavaScript, the Boolean data type recognizes only the literal true as true. Any other value is considered as false. Consider the following example −

    var str = 'abc'; 
    if(str) { 
       print('String is not empty'); 
    } else { 
       print('Empty String'); 
    } 

    The above snippet, if run in JavaScript, will print the message ‘String is not empty’ as the if construct will return true if the string is not empty.

    However, in Dart, str is converted to false as str != true. Hence the snippet will print the message ‘Empty String’ (when run in unchecked mode).

    Example

    The above snippet if run in checked mode will throw an exception. The same is illustrated below −

    void main() { 
       var str = 'abc'; 
       if(str) { 
    
      print('String is not empty'); 
    } else {
      print('Empty String'); 
    } }

    It will produce the following output, in Checked Mode −

    Unhandled exception: 
    type 'String' is not a subtype of type 'bool' of 'boolean expression' where 
       String is from dart:core 
       bool is from dart:core  
    #0 main (file:///D:/Demos/Boolean.dart:5:6) 
    #1 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:261) 
    #2 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:148)
    

    It will produce the following output, in Unchecked Mode −

    Empty String
    

    Note − The WebStorm IDE runs in checked mode, by default.

  • Strings

    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 −

    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,

    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,

    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,

    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,

    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,

    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,

    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,

    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
    1at(string, position)Returns the grapheme at the position of the given utf8 string. If position is greater than string length, then it returns nil
    2capitalize(string)Converts the first character in the given string to uppercase and the remainder to lowercase
    3contains?(string, contents)Checks if string contains any of the given contents
    4downcase(string)Converts all characters in the given string to lowercase
    5ends_with?(string, suffixes)Returns true if string ends with any of the suffixes given
    6first(string)Returns the first grapheme from a utf8 string, nil if the string is empty
    7last(string)Returns the last grapheme from a utf8 string, nil if the string is empty
    8replace(subject, pattern, replacement, options \\ [])Returns a new string created by replacing occurrences of pattern in subject with replacement
    9slice(string, start, len)Returns a substring starting at the offset start, and of length len
    10split(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
    11upcase(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 −

    IO.puts(<< 256 :: utf8 >>)

    The above program generates the following result −

    Ā
    

    We also have a function called is_binary that checks if a given variable is a binary. Note that only variables which are stored as multiples of 8bits are binaries.

    Bitstrings

    If we define a binary using the size modifier and pass it a value that is not a multiple of 8, we end up with a bitstring instead of a binary. For example,

    bs = << 1 :: size(1) >>
    IO.puts(bs)
    IO.puts(is_binary(bs))
    IO.puts(is_bitstring(bs))

    The above program generates the following result −

    << 1::size(1) >>
    false
    true
    

    This means that variable bs is not a binary but rather a bitstring. We can also say that a binary is a bitstring where the number of bits is divisible by 8. Pattern matching works on binaries as well as bitstrings in the same way.

  • String

    The String data type represents a sequence of characters. A Dart string is a sequence of UTF 16 code units.

    String values in Dart can be represented using either single or double or triple quotes. Single line strings are represented using single or double quotes. Triple quotes are used to represent multi-line strings.

    The syntax of representing string values in Dart is as given below −

    Syntax

    String  variable_name = 'value'  
    
    OR  
    
    String  variable_name = ''value''  
    
    OR  
    
    String  variable_name = '''line1 
    line2'''  
    
    OR  
    
    String  variable_name= ''''''line1 
    line2''''''
    

    The following example illustrates the use of String data type in Dart.

    void main() { 
       String str1 = 'this is a single line string'; 
       String str2 = "this is a single line string"; 
       String str3 = '''this is a multiline line string'''; 
       String str4 = """this is a multiline line string"""; 
       
       print(str1);
       print(str2); 
       print(str3); 
       print(str4); 
    }

    It will produce the following Output −

    this is a single line string 
    this is a single line string 
    this is a multiline line string 
    this is a multiline line string 
    

    Strings are immutable. However, strings can be subjected to various operations and the resultant string can be a stored as a new value.

    String Interpolation

    The process of creating a new string by appending a value to a static string is termed as concatenation or interpolation. In other words, it is the process of adding a string to another string.

    The operator plus (+) is a commonly used mechanism to concatenate / interpolate strings.

    Example 1

    void main() { 
       String str1 = "hello"; 
       String str2 = "world"; 
       String res = str1+str2; 
       
       print("The concatenated string : ${res}"); 
    }

    It will produce the following output −

    The concatenated string : Helloworld
    

    Example 2

    You can use “${}” can be used to interpolate the value of a Dart expression within strings. The following example illustrates the same.

    void main() { 
       int n=1+1; 
       
       String str1 = "The sum of 1 and 1 is ${n}"; 
       print(str1); 
       
       String str2 = "The sum of 2 and 2 is ${2+2}"; 
       print(str2); 
    }

    It will produce the following output −

    The sum of 1 and 1 is 2 
    The sum of 2 and 2 is 4
    

    String Properties

    The properties listed in the following table are all read-only.

    Sr.NoProperty & Description
    1codeUnitsReturns an unmodifiable list of the UTF-16 code units of this string.
    2isEmptyReturns true if this string is empty.
    3LengthReturns the length of the string including space, tab and newline characters.

    Methods to Manipulate Strings

    The String class in the dart: core library also provides methods to manipulate strings. Some of these methods are given below −

    Sr.NoMethods & Description
    1toLowerCase()Converts all characters in this string to lower case.
    2toUpperCase()Converts all characters in this string to upper case.
    3trim()Returns the string without any leading and trailing whitespace.
    4compareTo()Compares this object to another.
    5replaceAll()Replaces all substrings that match the specified pattern with a given value.
    6split()Splits the string at matches of the specified delimiter and returns a list of substrings.
    7substring()Returns the substring of this string that extends from startIndex, inclusive, to endIndex, exclusive.
    8toString()Returns a string representation of this object.
    9codeUnitAt()Returns the 16-bit UTF-16 code unit at the given index.
  • Numbers

    Dart numbers can be classified as −

    • int − Integer of arbitrary size. The int data type is used to represent whole numbers.
    • double − 64-bit (double-precision) floating-point numbers, as specified by the IEEE 754 standard. The double data type is used to represent fractional numbers

    The num type is inherited by the int and double types. The dart core library allows numerous operations on numeric values.

    The syntax for declaring a number is as given below −

    int var_name;      // declares an integer variable 
    double var_name;   // declares a double variable 
    

    Example

    void main() {
       // declare an integer
       int num1 = 10;             
    
     
    // declare a double value double num2 = 10.50; // print the values print(num1); print(num2); }

    It will produce the following output −

    10 
    10.5 
    

    Note − The Dart VM will throw an exception if fractional values are assigned to integer variables.

    Parsing

    The parse() static function allows parsing a string containing numeric literal into a number. The following illustration demonstrates the same −

    void main() { 
       print(num.parse('12')); 
       print(num.parse('10.91')); 
    }

    The above code will result in the following output −

    12 
    10.91
    

    The parse function throws a FormatException if it is passed any value other than numerals. The following code shows how to pass an alpha-numeric value to the parse() function.

    Example

    void main() { 
       print(num.parse('12A')); 
       print(num.parse('AAAA')); 
    }

    The above code will result in the following output −

    Unhandled exception: 
    FormatException: 12A 
    #0 num.parse (dart:core/num.dart:446) 
    #1 main (file:///D:/Demos/numbers.dart:4:13) 
    #2 _startIsolate.<anonymous closure> (dart:isolatepatch/isolate_patch.dart:261) 
    #3 _RawReceivePortImpl._handleMessage (dart:isolatepatch/isolate_patch.dart:148)
    

    Number Properties

    The following table lists the properties supported by Dart numbers.

    Sr.NoProperty & Description
    1hashcodeReturns a hash code for a numerical value.
    2isFiniteTrue if the number is finite; otherwise, false.
    3isInfiniteTrue if the number is positive infinity or negative infinity; otherwise, false.
    4isNanTrue if the number is the double Not-a-Number value; otherwise, false.
    5isNegativeTrue if the number is negative; otherwise, false.
    6signReturns minus one, zero or plus one depending on the sign and numerical value of the number.
    7isEvenReturns true if the number is an even number.
    8isOddReturns true if the number is an odd number.

    Number Methods

    Given below are a list of commonly used methods supported by numbers −

    Sr.NoMethod & Description
    1absReturns the absolute value of the number.
    2ceilReturns the least integer no smaller than the number.
    3compareToCompares this to other number.
    4FloorReturns the greatest integer not greater than the current number.
    5remainderReturns the truncated remainder after dividing the two numbers.
    6RoundReturns the integer closest to the current numbers.
    7toDoubleReturns the double equivalent of the number.
    8toIntReturns the integer equivalent of the number.
    9toStringReturns the string equivalent representation of the number.
    10truncateReturns an integer after discarding any fractional digits.
  • Decision Making

    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 −

    Decision Making

    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
    1if statementAn 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.
    2if..else statementAn if statement can be followed by an optional else statement(within the do..end block), which executes when the Boolean expression is false.
    3unless statementAn unless statement has the same body as an if statement. The code within unless statement executes only when the condition specified is false.
    4unless..else statementAn 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.
    5condA 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.
    6caseCase 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.
  • Pattern Matching

    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,

    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 −

    [_, [_, {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.