Elixir Basic



  • What is Elixir?

    Elixir is a dynamic, functional language that build on top of Erlang Virtual Machine, a distributed and fault-tolerant systems with low-latency. It was designed for building scalable and maintainable web applications as well as embedded software. Elixir wrap functional programming with immutable state and an actor-based approach to concurrency.

    All About Transforming Data

    If you come from an object-oriented world, then you are used to thinking in terms of classes and their instances. A class defines behavior, and objects hold state. By invoking a method on an object and passing it other object we update the object's status. In the world of OOP data hiding is our goal.

    But somethime we don't want to model a complext and abstract class hierarchies, we just wanted to get thing done. For example somewhere down the line I want to read a text file, doing calculation on its content, write it back as csv file and send it back as an HTTP response. I don't want to hide data. I want to transform it, combine with pipline and immutable state making it likes bread and butter.

    Installing Elixir

    Before we dive into elixir, lets install it. Go to https://elixir-lang.org/install.html and follow instruction for your specific OS. To confirm that you have elixir up and running type this into your terminal.

    $ elixir -v
    

    And you should be something similar to this

    $ Erlang/OTP 20 [erts-9.1.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
    
    Elixir 1.5.2
    

    Basic Types

    Elixir's build-in types are: Integer, Float, Boolean, Atom, String, List, Map, Tuple and Function. There are also system type like PID, ports and references.

    iex> 12 # integer
    iex> 1.15 # float
    iex> true # boolean
    iex> :name # atom
    iex> "string" # string
    iex> "Hello #{name}" # string interpolation
    iex> [1, 2, 3, 4] # list
    iex> {1, 2, 3} # tuple
    iex> %{a: 10, b: 20} # map
    iex> %{"a" => 10, "b" => 20} # map
    iex> add = fn a, b -> a + b end # anonymous function
    
    Boolean

    Bollean is actually an atom with the name true and false respectively

    iex> :true == true
    true
    iex> :false == false
    true
    
    List

    Because list implemented as linked list we can't randomly access element of a list using regular indexing syntax list we used to. To get the first element of the list we can use the [head | tail] syntax with head being the first element and tail is a list rest of remaning elements.

    iex> [h | t] = [1, 2, 3, 4, 5]
    iex> h
    1
    iex> t
    [2, 3, 4, 5]
    

    we can use this to create a function that can help us get element at any given index, but fortuantely elixir has a build-in module that deal with this problem called Enum

    iex> Enum.at([1,2,3], 2)
    3
    

    Tuple

    Tuple is just like list except elements are store contiguously in memory. Elixir use curly brackets syntax to define tuple

    iex> t = {:ok, "I'm OK"}
    {:ok, "I'm OK"}
    iex> elem(t, 0)
    :ok
    iex> put_elem(t, 0, :error)
    {:error, "I'm OK"}
    

    Map

    Maps are the key-value data structure just like Hash in ruby. For example

    iex> m = %{:name => "Jonh", :like => "Apple"}
    iex> Map.keys(m)
    [:name, :like]
    iex> Map.values(m)
    ["Jonh", "Apple"]
    iex> m[:name]
    "Jonh"
    iex> m.name
    "Jonh"
    iex> Map.drop(m, [:like])
    %{name: "Jonh"}
    

    When a key in the map is atom we can use special syntax like this

    iex> m = %{a: 10, b: 20}
    %{a: 10, b: 20}
    

    The simplest way to update a map is with this syntax

    iex> m = %{a: 10, b: 20}
    %{a: 10, b: 20}
    iex> m1 = %{m | b: 30}
    %{a: 10, b: 30}
    
    Keyword List

    This is a special kind of list that each element is a tuple of two elements. The first element of the tuple is a key and second is a value. The key for keyword list must be atom type.

    iex> l = [a: 10, b: 20]
    iex> [a, b] = l # a = {:a, 10}, b = {:b, 20}
    

    Pattern Matching

    Let's fire up the interactive elixir shell by typing iex in your terminal and type in the really simple code like below.

    iex> x = 10
    iex> y = 15 + x
    

    Looking at this code snippet most of us would think that we've just assigned 10 to x , adding 15 to x and assigned the result back to y, but this is not the case in elixir. The equal sign is not an assignment, but instead it's like an assertion. Elixir call the = sign a match operator. If elixir can find a way of making the left hand side equal to the right hand side value then it's success otherwise it's gonna blow up. In the case of the first line because x is a variable elixir try to match by bind value 10 to this variable as a result x get the value of 10.

    iex> x = 10
    iex> 20 = x
    ** (MatchError) no match of right hand side value: 10
    

    If your really not convince that it is a match operator then lets look at this example. In most programming language this will result in syntax error, but as you can see it works perfectly fine in elixir.

    iex> a = 1
    iex> 1 = a
    1
    

    Matching doesn't limit to just simple value we can also use it to match list, tuple, keyword list, map. For example

    iex> [a, b] = [1, [2, 3]] # a = 1, b = [2, 3]
    iex> {:ok, value} = {:ok, "I'm OK"} # value = "I'm OK"
    iex> %{name: n, age: a} = %{name: "Jonh", age: 25} # n = "Jonh", a = 25
    iex> %{name: n, age: a} = %{name: "Jonh"}
    ** (MatchError) no match of right hand side value: %{name: "Jonh"}
    

    Function

    In elixir function must be live inside of a module and define using def keyword or defp for private function. To define a module we use defmodule keyword.

    defmodule MyModule do
      def my_func(args), do: # inline function
      def my_func(args) do
        # multi line function
      end
    end
    

    there is also another kind of function called anonymous function that we've just seen in basic types section. To invoke anonymous function we need to use .() notation.

    iex> add = fn a, b -> a + b end
    #Function<12.99386804/2 in :erl_eval.expr/5>
    iex> add.(10,20)
    30
    
    Pattern Match on Function

    Remember that assignment is just pattern match in elixir? That is also hold true when it come to function parameters. Let's look at the example on how to count all elements inside a list with recursive function

    defmodule MyList do
      def len([]), do: 0
      def len([_h | t]), do: 1 + len(t)
    end
    
    iex> MyList.len([1,2,3,4,5])
    5
    

    The second definition of len wii slice down a list one element at a time and goes into a recursion until the list is empty which will then match the definition of the first function. Because the tail of a list is another list excluding the first element so list with one element is always terminate by an empty list. With this trick in your bag you can almost write any complex conditional logic without event using any if condition at all.

    Control Flow

    If and unless

    if and unless is not a language keyword construct, but rather a macro that expand to case statement. It adds syntactic sugar to make it more convenient.

    if condition do
      # true part
    else
      # false part
    end
    
    unless condition do
      # false part
    else
      # true part
    end
    
    case

    case lets you test a value against a set of patterns, executes the code associated with the first one that matches, and returns the value of that code. The pat- terns may include guard clauses.

    case File.open("text.txt") do
      {:ok, file} ->
        # sucessfully open a file
      {:error, reason} ->
        # failed to open file
      _ ->
        # guard clause if for some reason the above pattern doesn't match
    end
    

    Getting Help

    You can get help with a nice example written in markdown on a module by using h function in iex like this.

    iex> h Enum
                                          Enum
    Provides a set of algorithms that enumerate over enumerables according to the
    Enumerable protocol.
    
        iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
        [2, 4, 6]
    
    Some particular types, like maps, yield a specific format on enumeration. For
    example, the argument is always a {key, value} tuple for maps:
    
        iex> map = %{a: 1, b: 2}
        iex> Enum.map(map, fn {k, v} -> {k, v * 2} end)
        [a: 2, b: 4]
    
    Note that the functions in the Enum module are eager: they always start the
    enumeration of the given enumerable. The Stream module allows lazy enumeration
    of enumerables and provides infinite streams.
    
    Since the majority of the functions in Enum enumerate the whole enumerable and
    return a list as result, infinite streams need to be carefully used with such
    functions, as they can potentially run forever. For example:
    
        Enum.each Stream.cycle([1, 2, 3]), &IO.puts(&1)
    

    to get help on a function you need to specify both function name and it's arity (number of argument)

    iex> h Enum.at/2
                       def at(enumerable, index, default \\ nil)
    
    Finds the element at the given index (zero-based).
    
    Returns default if index is out of bounds.
    
    A negative index can be passed, which means the enumerable is enumerated once
    and the index is counted from the end (e.g. -1 finds the last element).
    
    Note this operation takes linear time. In order to access the element at index
    index, it will need to traverse index previous elements.
    
    ## Examples
    
        iex> Enum.at([2, 4, 6], 0)
        2
    
        iex> Enum.at([2, 4, 6], 2)
        6
    
        iex> Enum.at([2, 4, 6], 4)
        nil
    
        iex> Enum.at([2, 4, 6], 4, :none)
        :none
    
    

    Nguồn: Viblo


Hãy đăng nhập để trả lời
 

Có vẻ như bạn đã mất kết nối tới LaptrinhX, vui lòng đợi một lúc để chúng tôi thử kết nối lại.