elixir-tips
Search…
Killer Elixir-Tips

Killer Elixir-Tips

Elixir Tips and Tricks from the Experience of Development. Each part consists of 10 Unique Tips and Tricks with a clear explanation with live examples and outputs. These tips will speed up your development and save you time in typing code as well.
You can read specific parts with following links...
</div>
All Photos by Form on Unsplash
Checkout some cherry picked tips from the all Parts

Taste Before you Eat :)

iex Custom Configuration - iex Decoration

Copy the content into a file and save the file as .iex.exs in your ~ home directory and see the magic. You can also download the file HERE
1
# IEx.configure colors: [enabled: true]
2
# IEx.configure colors: [ eval_result: [ :cyan, :bright ] ]
3
IO.puts IO.ANSI.red_background() <> IO.ANSI.white() <> " ❄❄❄ Good Luck with Elixir ❄❄❄ " <> IO.ANSI.reset
4
Application.put_env(:elixir, :ansi_enabled, true)
5
IEx.configure(
6
colors: [
7
eval_result: [:green, :bright] ,
8
eval_error: [[:red,:bright,"Bug Bug ..!!"]],
9
eval_info: [:yellow, :bright ],
10
],
11
default_prompt: [
12
"\e[G", # ANSI CHA, move cursor to column 1
13
:white,
14
"I",
15
:red,
16
"❤" , # plain string
17
:green,
18
"%prefix",:white,"|",
19
:blue,
20
"%counter",
21
:white,
22
"|",
23
:red,
24
"▶" , # plain string
25
:white,
26
"▶▶" , # plain string
27
# ❤ ❤-»" , # plain string
28
:reset
29
] |> IO.ANSI.format |> IO.chardata_to_string
30
31
)
Copied!
img

Creating Custom Sigils and Documenting

Each x sigil calls its respective sigil_x definition
Defining Custom Sigils
1
defmodule MySigils do
2
#returns the downcasing string if option l is given then returns the list of downcase letters
3
def sigil_l(string,[]), do: String.downcase(string)
4
def sigil_l(string,[?l]), do: String.downcase(string) |> String.graphemes
5
6
#returns the upcasing string if option l is given then returns the list of downcase letters
7
def sigil_u(string,[]), do: String.upcase(string)
8
def sigil_u(string,[?l]), do: String.upcase(string) |> String.graphemes
9
end
Copied!
usage
Load the module into iex
1
iex> import MySigils
2
iex> ~l/HELLO/
3
"hello"
4
iex> ~l/HELLO/l
5
["h", "e", "l", "l", "o"]
6
iex> ~u/hello/
7
"HELLO"
8
iex> ~u/hello/l
9
["H", "E", "L", "L", "O"]
Copied!

Custom Error Definitions

Define Custom Error
1
defmodule BugError do
2
defexception message: "BUG BUG .." # message is the default
3
end
Copied!
Usage
1
iex bug_error.ex
2
3
iex> raise BugError
4
** (BugError) BUG BUG ..
5
iex> raise BugError, message: "I am Bug.." #here passing the message dynamic
6
** (BugError) I am Bug..
Copied!

Writing Protocols

Define a Protocol
A Protocol is a way to dispatch to a particular implementation of a function based on the type of the parameter. The macros defprotocol and defimpl are used to define Protocols and Protocol implementations respectively for different types in the following example.
1
defprotocol Triple do
2
def triple(input)
3
end
4
5
defimpl Triple, for: Integer do
6
def triple(int) do
7
int * 3
8
end
9
end
10
11
defimpl Triple, for: List do
12
def triple(list) do
13
list ++ list ++ list
14
end
15
end
Copied!
Usage
Load the code into iex and execute
1
iex> Triple.triple(3)
2
9
3
Triple.triple([1, 2])
4
[1, 2, 1, 2, 1, 2]
Copied!

Ternary Operator

There is no ternary operator like true ? "yes" : "no" . So, the following is suggested.
1
"no" = if 1 == 0, do: "yes", else: "no"
Copied!

Advantage of Kernel.||

When using pipelines, sometimes we break the pipeline for or operation. For example:
1
result = :input
2
|> do_something
3
|> do_another_thing
Copied!
1
# Bad
2
result = (result || :default_output)
3
|> do_something_else
Copied!
Indeed, || is only a shortcut for Kernel.|| . We can use Kernel.|| in the pipeline instead to avoid breaking the pipeline.
The code above will be:
1
result = :input
2
|> do_something
3
|> do_another_thing
4
|> Kernel.||(:default_output) #<-- This line
5
|> do_something_else
Copied!
This above tip is from qhwa

Checking Out Code Grouping

Code grouping stands for something great. It shows you how your code is grouped when you write multiple lines of code in single line with out using braces. It will be more clear with the following example.
1
one 1 |> two()
Copied!
If you want to see how this line of code is grouped into, you can check in the following format..
1
quote(do: one 1 |> two()) |> Macro.to_string |> IO.puts
2
one(1 |> two())
Copied!
So, by using the quote and Macro.to_string you can see how our code was grouped into.
This tip came out in discussion with the creator of Ecto MichalMuskala in the Elixir forum.

Elixir Short Circuit Operators && — ||

These replaces the nested complicated conditions. These are my best friends in the situations dealing with more complex comparisons. Trust me you gonna love this.
The || operator always returns the first expression which is true. Elixir doesn’t care about the remaining expressions, and won’t evaluate them after a match has been found.
||
1
false || nil || :blackode || :elixir || :jose
Copied!
Here if you observe the first expression is false next nil is also false in elixir next :blackode which evaluates to true and its value is returned immediately with out evaluating the :elixir and :jose . Similarly if all the statements evaluates to false the last expression is returned.
&&
1
iex> true && :true && :elixir && 5
2
5
3
iex> nil && 100
4
nil
5
iex> salary = is_login && is_admin && is_staff && 100_000
Copied!
This && returns the second expression if the first expression is true or else it returns the first expression with out evaluating the second expression. In the above examples the last one is the situation where we encounter to use the && operator.

Comparing two different data types

I have self experience with this. When I was a novice in elixir, I just compared "5" > 4 unknowingly by an accident and to my surprise it returned with true.
In Elixir every term can compare with every other term. So one has to be careful in comparisons.
img
1
iex> x = "I am x "
2
"I am x "
3
iex> x > 34
4
true
5
iex> x > [1,2,3]
6
true
7
iex> [1, 2, 3] < 1234567890
8
false
Copied!
Order of Comparison
number < atom < reference < fun < port < pid < tuple < map < list < bitstring (binary)

Arithmetic Operators as Lambda functions

When I see this first time, I said to my self “Elixir is Crazy”. This tip really saves time and it resembles your smartness. In Elixir every operator is a macro. So, we can use them as lambda functions.
1
iex> Enum.reduce([1,2,3], 0, &+/2)
2
6
3
iex> Enum.reduce([1,2,3], 0, &*/2)
4
0
5
iex> Enum.reduce([1,2,3], 3, &*/2)
6
18
7
iex> Enum.reduce([1,2,3], 3, &-/2)
8
-1
9
iex> Enum.reduce([1,2,3], 3, &//2)
10
0.5
Copied!

Binary Pattern Matching

This is my recent discovery. I always encounter a situation like converting "$34.56" which is a string and I suppose do arithmetic operations. I usually do something like this before binary pattern matching..
img
1
iex> value = "$34.56"
2
iex ... |> String.split("quot;)
3
iex ... |> tl
4
iex ... |> List.first
5
iex ... |> String.to_float
6
34.56
Copied!
Tip Approach
This tip makes my day easy. I recently used this is in one of my projects.
1
iex> "quot; <> value = "$34.56"
2
"$34.56"
3
iex> String.to_float value
4
34.56
Copied!

Must Know the Differences of Map keys as :atoms and binary(strings)

Before I let you to use this tip, I just want to remind you that :atoms are not garbage collected. Atom keys are great! If you have a defined atoms, you are in no danger. What you should not do is converting user supplied input into atoms without sanitizing them first because it can lead to out of memory. You should also be cautious if you create dynamic atoms in your code.
But, you can use the . to retrieve the data from the keys as map.key unlike the usual notation like map["key"] . That really saves on typing. But, I don’t encourage this because, as programmers we should really care about memory.
img
1
iex> map = %{name: "blackode", blog: "medium"}
2
%{blog: "medium", name: "blackode"}
3
4
iex> map.name
5
"blackode"
6
7
iex> map.blog
8
"medium"
Copied!
Be sure that when you try to retrieve a key with . form which is not present in the map, it will raise a key error instead of returning the nil unlike the map["key"] which returns nil if key is not present in a map.
1
iex> map["age"]
2
nil
Copied!
1
iex> map.age
2
Bug Bug ..!!** (KeyError) key :age not found in: %{blog: "medium", name: "blackode"}
3
Bug Bug ..!!
Copied!

Add Some Color To Your Console Prints

Elixir >=1.4.0 has ANSI color printing option to console. You can have great fun with colors. You can also provide background colors.
1
iex> import IO.ANSI
2
iex> IO.puts red <> "red" <> green <> " green" <> yellow <> " yellow" <> reset <> " normal"
3
iex> IO.puts Enum.join [red, "red", green, " green", yellow, " yellow", reset, " normal"]
4
red green yellow normal
Copied!
The red prints in red color, green in green color, yellow in yellow color and normal in white. Have fun with colors…
For more details on color printing check Printex module which I created for fun in Elixir.
img
img

Functional Macros as Guard Clauses

We cannot make use of the functions as guard clauses in elixir. It means, when cannot accept custom defined functions. Consider the following lines of code…
1
defmodule Hello do
2
def hello(name, age) when is_kid(age) do
3
IO.puts "Hello Kid #{name}"
4
end
5
6
def hello(name, age) when is_adult(age) do
7
IO.puts "Hello Mister #{name}"
8
end
9
10
def is_kid age do
11
age < 12
12
end
13
14
def is_adult age do
15
age > 18
16
end
17
end
Copied!
Here we defined a module Hello and a function hello that takes two parameters of name and age. So, based on age I am trying IO.putsaccordingly. If you do so you will get an error saying….
1
** (CompileError) hello.ex:2: cannot invoke local is_kid/1 inside guard
2
hello.ex:2: (module)
Copied!
This is because when cannot accept functions as guards. We need to convert them to macros Lets do that…
1
defmodule MyGuards do
2
3
defmacro is_kid age do
4
quote do: unquote(age) < 12
5
end
6
7
defmacro is_adult age do
8
quote do: unquote(age) > 18
9
end
10
11
end
12
# order of module matters here.....
13
defmodule Hello do
14
15
import MyGuards
16
17
def hello(name, age) when is_kid(age) do
18
IO.puts "Hello Kid #{name}"
19
end
20
21
def hello(name, age) when is_adult(age) do
22
IO.puts "Hello Mister #{name}"
23
end
24
25
def hello(name, age) do
26
IO.puts "Hello Youth #{name}"
27
end
28
29
end
Copied!
In the above lines of code, we wrapped all our guards inside a module MyGuards and make sure the module is top of the module Hello so, the macros first gets compiled. Now compile and execute you will see the following output..
1
iex> Hello.hello "blackode", 21
2
Hello Mister blackode
3
:ok
4
iex> Hello.hello "blackode", 11
5
Hello Kid blackode
6
:ok
Copied!
Starting on Elixir v1.6, you can use defguard/1.
The defguard is also a macro. You can also create private guards with defguardp. Hope, you got the point here. Consider the following example.
NOTE: The defguard and defguardp should reside inside the module like other macros. It raises a compile time error, if some thing that don't fit in the guard clause section when.
Suppose, you want to check the given number is either three or five, you can define the guard as following.
1
defmodule Number.Guards do
2
defguard is_three_or_five(number) when (number===3) or (number===5)
3
end
Copied!
Usage
1
import Number.Guards
2
defmodule Hello do
3
def check_favorite_number(num) when is_three_or_five(num) do
4
IO.puts "The given #{num} is on of my favourite numbers"
5
end
6
def check_favorite_number(_num), do: IO.puts "Not my favorite number"
7
end
Copied!
You can also use them inside your code logic as they results boolean value.
1
iex> import Number.Guards
2
Number.Guards
3
4
iex> is_three_or_five(5)
5
true
6
7
iex> is_three_or_five(3)
8
true
9
10
iex> is_three_or_five(1)
11
false
Copied!
Check the following execution screen shot.
ScreenShot Defguard Execution

Finding whether Module was loaded or not

Sometimes, we have to make sure that certain module is loaded before making a call to the function. We are supposed to ensure the module is loaded.
1
Code.ensure_loaded? <Module>
Copied!
1
iex> Code.ensure_loaded? :kernel
2
true
3
iex> Code.ensure_loaded :kernel
4
{:module, :kernel}
Copied!
Similarly we are having ensure_compile to check whether the module is compiled or not…

Converting Binary to Capital Atom

Elixir provides a special syntax which is usually used for module names. What is called a module name is an uppercase ASCII letter followed by any number of lowercase or uppercase ASCII letters, numbers, or underscores.
This identifier is equivalent to an atom prefixed by Elixir. So in the defmodule Blackode example Blackode is equivalent to :"Elixir.Blackode"
When we use String.to_atom "Blackode" it converts it into :Blackode But actually we need something like “Blackode” to Blackode. To do that we need to use Module.concat
1
iex(2)> String.to_atom "Blackode"
2
:Blackode
3
iex(3)> Module.concat Elixir,"Blackode"
4
Blackode
Copied!
In Command line applications whatever you pass they convert it into binary. So, again you suppose to do some casting operations …

Pattern match [ vs ] destructure.

We all know that = does the pattern match for left and right side. We cannot do [a, b, c] = [1, 2, 3, 4] this raise a MatchError
1
iex(11)> [a, b, c] = [1, 2, 3, 4]
2
** (MatchError) no match of right hand side value: [1, 2, 3, 4]
Copied!
We can use destructure/2 to do the job.
1
iex(1)> destructure [a, b, c], [1, 2, 3, 4]
2
[1, 2, 3]
3
iex(2)> {a, b, c}
4
{1, 2, 3}
Copied!
If the left side is having more entries than in right side, it assigns the nil value for remaining entries..
1
iex> destructure([a, b, c], [1])
2
iex> {a, b, c}
3
{1, nil, nil}
Copied!

The Goodness of Data decoration [ inspect with :label ] option

We can decorate our output with inspect and label option. The string of label is added at the beginning of the data we are inspecting.
1
iex(1)> IO.inspect [1, 2, 3], label: "the list "
2
the list : [1, 2, 3]
3
[1, 2, 3]
Copied!
If you closely observe this it again returns the inspected data. So, we can use them as intermediate results in |> pipe operations like following……
1
[1, 2, 3]
2
|> IO.inspect(label: "before change")
3
|> Enum.map(&(&1 * 2))
4
|> IO.inspect(label: "after change")
5
|> length
Copied!
You will see the following output
1
before change: [1, 2, 3]
2
after change: [2, 4, 6]
3
3
Copied!

Piping Anonymous functions

We can pass the anonymous functions in two ways. One is directly using &like following..
1
[1, 2, 3, 4, 5]
2
|> length()
3
|> (&(&1*&1)).()
Copied!
This is the most weirdest approach. How ever, we can use the reference of the anonymous function by giving its name.
1
square = & &1 * &1
2
[1, 2, 3, 4, 5]
3
|> length()
4
|> square.()
Copied!
The above style is much better than previous . You can also use fn to define anonymous functions.

Retrieve Character Integer Codepoints — ?

We can use ? operator to retrieve character integer codepoints.
1
iex> ?a
2
97
3
iex> ?#
4
35
Copied!
The following two tips are mostly useful for beginners…

Performing Subtraction on Lists

We can perform the subtraction over lists for removing the elements in list.
1
iex> [1, 2, 3, 4.5] -- [1, 2]
2
[3, 4.5]
3
iex> [1, 2, 3, 4.5, 1] -- [1]
4
[2, 3, 4.5, 1]
5
iex> [1, 2, 3, 4.5, 1] -- [1, 1]
6
[2, 3, 4.5]
7
iex> [1, 2, 3, 4.5] -- [6]
8
[1, 2, 3, 4.5]
Copied!
We can also perform same operations on char lists too..
1
iex(12)> 'blackode' -- 'ode'
2
'black'
3
iex(13)> 'blackode' -- 'z'
4
'blackode'
Copied!
If the element to subtract is not present in the list then it simply returns the list.

Running Multiple Mix Tasks

1
mix do deps.get,compile
Copied!
You can run multiple tasks by separating them with coma ,
How ever you can also create aliases in your mix project in a file called mix.exs .
The project definition looks like the following way when you create one using a mix tool.
1
def project do
2
[app: :project_name,
3
version: "0.1.0",
4
elixir: "~> 1.4-rc",
5
build_embedded: Mix.env == :prod,
6
start_permanent: Mix.env == :prod,
7
deps: deps()]
8
end
Copied!
You are also allowed to add some extra fields…
Here you have to add the aliases field.
1
[
2
aliases: aliases()
3
]
Copied!
Don’t forget to add , at the end when you add this in the middle of list .
The aliases() should return the key-value list.
1
defp aliases do
2
[
3
"ecto.setup": ["ecto.create", "ecto.migrate", "ecto.seed"]
4
]
5
end
Copied!
So, whenever you run the mix ecto.setup the three tasks ecto.create, ecto.migrate and ecto.seed will run one after the other.
You can also add them directly as following unlike I did with private function.
1
def project do
2
[app: :project_name,
3
version: "0.1.0",
4
aliases: ["ecto.setup": ["ecto.create", "ecto.migrate", "ecto.seed"]]
5
.....
6
end
Copied!

Accessing the Documentation

Elixir stores the documentation inside the bytecode in a memory. You access the documentation with the help of Code.get_docs/2 function . This means, the documentation accessed when it is required, but not when it is loaded in the virtual machine like iex
Suppose you defined a module in memory like ones you defined in IEx, cannot have their documentation accessed as they do not have their bytecode written to disk.
Let us check this…
Create a module with name test.ex with the following code. You can copy and paste it.
1
defmodule Test do
2
@moduledoc """
3
This is the test module docs
4
"""
5
6
@doc """
7
This is the documentation of hello function
8
"""
9
def hello do
10
IO.puts "hello"
11
end
12
end
Copied!
Now stay in the directory where your file exists and run the command
1
$ iex test.ex
Copied!
Now you can access the function definitions but not the documentation.
1
iex> Test.hello
2
hello
3
:ok
Copied!
That means the code is compiled but documentation is not stored in the memory. So, you cannot access the docs. Lets check that…
1
iex> Code.get_docs Hello, :moduledoc
2
nil
Copied!
You will see the output as nil when you are trying to access the docs of the module you have created so far. This is because, the bytecode is not available in disk. In simple way beam file is not present. Lets do that...
Press Ctrl+C twice so you will come out of the shell and this time you run the command as
1
$ elixirc test.ex
Copied!
After running the command, you will see a file with name Elixir.Test.beam . Now the bytecode for the module Test is available in memory. Now you can access the documentation as follows...
1
$ iex
2
iex> Code.get_docs Test, :moduledoc
3
{3, "This is the test module docs\n"}
Copied!
The output is tuple with two elements. The first element is the line number of the documentation it starts and second element is the actual documentation in the binary form.
You can read more about this function here

Dynamic Function Name in Elixir Macro

The hack is name of a function should be an atom instead of binary.
1
defmacro gen_function(fun_name) do
2
quote do
3
def unquote(:"#{fun_name}")() do
4
# your code...
5
end
6
end
7
end
Copied!

Printing List as List without ASCII-Encoding

You know that when the list contains all the numbers as ASCII values, it will list out those values instead of the original numbers. Lets check that…
1
iex> IO.inspect [97, 98]
2
'ab'
3
'ab'
Copied!
The code point of a is 97 and b is 98 hence it is listing out them as char_list. However you can tell the IO.inspect to list them as list itself with option char_lists: :as_list .
1
iex> IO.inspect [97, 98], charlists: :as_lists
2
[97, 98]
3
'ab'
Copied!
Open iex and type h Inspect.Opts, you will see that Elixir does this kind of thing with other values as well, specifically structs and binaries.

Fetching out file name and line number the expression is on

1
defmacro __ENV__()
Copied!
This macro gives the current environment information. You can get the information like current filename line function and others…
1
iex(4)> __ENV__.file
2
"iex"
3
4
iex(5)> __ENV__.line
5
5
Copied!

Creating Manual Pids for Unit Testing

You can create the pid manually in Elixir with pid function. This comes with two flavors.
def pid(string)
Creates the pid from the string.
1
iex> pid("0.21.32")
2
#PID<0.21.32>
Copied!
def pid(a, b, c)
Creates a PID with 3 non negative integers passed as arguments to the function.
1
iex> pid(0, 21, 32)
2
#PID<0.21.32>
Copied!
Why do you create the pids manually?
Suppose, you are writing a library and you want to test one of your functions for the type pid, then you can create one and test with it.
You cannot create the pid like assigning pid = #PID<0.21.32> because # is considered as comment here.
1
iex(6)> pid = #PID<0.21.32>
2
...(6)>
Copied!
When you do like above, iex shell just wait for more input as #PID<0.21.32> is treated as comment.
Now you enter another data to complete the expression. The entered value is the value of the pid. Lets check that…
1
iex(6)> pid = #PID<0.21.32> # here expression is not complete
2
...(6)> 23 # here we are giving the value 23
3
23 # expression is complete
4
iex(7)> pid
5
23
Copied!

Replacing the String with global option

The String.replace function will replace the given the pattern with replacing pattern. By default, it replaces all the occurrences of the pattern. Lets check that…
3
4
iex(2)> String.replace str,"@","#"
5
"hello#hi.com, blackode#medium.com
Copied!
The String.replace str, "@", "#"is same as String.replace str, "@", "#", global: true
But, if you want to replace only the first occurrence of the pattern, you need to pass the option global: false . So, it replaces only the first occurrence of @ . Lets check that…
1
iex(3)> String.replace str, "@", "#", global: false
2
"hello#hi.com, [email protected]"
Copied!
Here only first @ is replaced with #.

Memory Usage

You can check the memory usage (in bytes) with :erlang.memory
1
iex(1)> :erlang.memory
2
[total: 16221568, processes: 4366128, processes_used: 4364992, system: 11855440,
3
atom: 264529, atom_used: 250685, binary: 151192, code: 5845369, ets: 331768]
Copied!
However, you can pass option like :erlang.memory :atom to get the memory usage of atoms.
1
iex(2)> :erlang.memory :atom
2
264529
Copied!

Picking out the elements in List

We all know that a proper list is a combination of head and tail like [head | tail] . We can use the same principle for picking out the elements in the list like the following way…
1
iex> [first, second, third, fourth | _rest] = [1, 2, 3, 4, 5, 6, 7]
2
[1, 2, 3, 4, 5, 6, 7]
3
iex> first
4
1
5
iex> {second, third, fourth}
6
{2, 3, 4}
Copied!

The combination effect of get_in /Access.all()

We all know that the get_in function is used to extract the key which is deeper inside the map by providing the list with keys like a following way…
1
iex> user = %{"name" => {"first_name" => "blackode", "last_name" => "john" }}
2
%{"name" => %{"first_name" => "blackode", "last_name" => "john"}}
3
iex > get_in user, ["name", "first_name"]
4
"blackode"
Copied!
But, if there is a list of maps [maps] where you have to extract first_name of the each map, generally we go for enum . We can also achieve this by using the get_in and Access.all()
1
iex> users=[%{"user" => %{"first_name" => "john", "age" => 23}},
2
%{"user" => %{"first_name" => "hari", "age" => 22}},
3
%{"user" => %{"first_name" => "mahesh", "age" => 21}}]
4
# that is a list of maps
5
iex> get_in users, [Access.all(), "user", "age"]
6
[23, 22, 21]
7
8
iex> get_in users, [Access.all(), "user", "first_name"]
9
["john", "hari", "mahesh"]
Copied!
Note: If the key is not present in map, then it returns nil Warning: When you use the get_in along with Access.all() , as the first value in the list of keys like above, the users datatype should be list. If you pass the map, it returns the error.
1
iex(17)> list = [%{name: "john"}, %{name: "mary"}, %{age: 34}]
2
[%{name: "john"}, %{name: "mary"}, %{age: 34}]
3
iex(18)> get_in(list, [Access.all(), :name])
4
["john", "mary", nil]
Copied!
In the above lines of code returns the nil for key which is not in the map.
1
iex(19)> get_in(%{name: "blackode"}, [Access.all(), :name])
2
** (RuntimeError) Access.all/0 expected a list, got: %{name: "blackode"}
3
(elixir) lib/access.ex:567: Access.all/3
Copied!
In the above lines of code returns the error for passing map .
However, you can change the position of the Access.all() in the list. But the before key should return the list. Check the following lines of code.
Deep Dive
We can also use the Access.all() with functions like update_in, get_and_update_in, etc.. For instance, given a user with a list of books, here is how to deeply traverse the map and convert all book names to uppercase:
1
iex> user = %{name: "john", books: [%{name: "my soul", type: "tragedy"}, %{name: "my heart", type: "romantic"}, %{name: "my enemy", type: "horror"}]}
2
iex> update_in user, [:books, Access.all(), :name], &String.upcase/1
3
%{books: [%{name: "MY SOUL", type: "tragedy"}, %{name: "MY HEART", type: "romantic"}, %{name: "MY ENEMY", type: "horror"}], name: "john"}
4
iex> get_in user, [:books, Access.all(), :name]
5
["my soul", "my heart", "my enemy"]
Copied!
Here, user is not a list unlike in the previous examples where we passed the users as a list. But, we changed the position of Access.all() and inside the list of keys [:books, Access.all(), :name], the value of the key :books should return the list, other wise it raises an error.

Data Comprehension along with filters using FOR

We achieve the data comprehension through for x <- [1, 2, 3], do: x + 1 . But we can also add the comprehension along with filter.
General Usage
1
iex> for x <- [1, 2, 3, 4], do: x + 1
2
[2, 3, 4, 5]
3
# that is how we use in general lets think out of the box
Copied!
With filters
Here I am using two lists of numbers and cross product over the lists and filtering out the product which is a odd number.
1
iex> for x <- [1, 2, 3, 4], y <- [5, 6, 7, 8], rem(x * y, 2) == 0, do: {x, y, x * y}
2
[{1, 5, 5}, {1, 7, 7}, {3, 5, 15}, {3, 7, 21}]
3
#here rem(x * y, 2) is acting as a filter.
Copied!

Comprehension with binary strings.

Comprehension with binary is little different. You supposed to wrap inside <<>>
Lets check that…
1
iex> b_string = <<"blackode">>
2
"blackode"
3
iex> for << x <- b_string >>, do: x + 1
4
'cmbdlpef'
5
#here it is printing out the letter after every letter in the "blackode"
Copied!
Did you observe that x <- b_string is just changed something like << x <- b_string >> to make the sense.

Advanced Comprehension IO.stream

Here we are taking the elixir comprehension to the next level. We read the input from the keyboard and convert that to upcase and after that it should wait for another entry.
1
for x <- IO.stream(:stdio, :line), into: IO.stream(:stdio, :line), do: String.upcase(x)
Copied!
Basically IO.stream(:stdio, :line) will the read a line input from the keyboard.
1
iex> for x <- IO.stream(:stdio, :line), into: IO.stream(:stdio, :line), do: String.upcase(x)
2
hello
3
HELLO
4
hi
5
HI
6
who are you?
7
WHO ARE YOU?
8
blackode
9
BLACKODE
10
^c ^c # to break
Copied!

Single Line Multiple module aliasing

We can also alias multiple modules in one line:
1
alias Hello.{One,Two,Three}
2
3
#The above line is same as the following
4
alias Hello.One
5
alias Hello.Two
6
alias Hello.Three
Copied!

Importing Underscore Functions from a Module

By default the functions with _ are not imported. However, you can do that by importing them with :only explicitly.
1
import File.Stream, only: [__build__: 3]
Copied!

Extracting Project Information

1
Mix.Project.config[:version] # returns the version
2
Mix.Project.config[:app] # returns app name
Copied!
You have to be inside the mix project when you are trying. See this in action…

Inner Binary Representation of String

This is a common trick in elixir . You have to concatenate the null byte <<0>> to a string that you want to see its inner binary representation like in the following way…
1
iex> “hello” <> <<0>>
2
<<104, 101, 108, 108, 111, 0>>
Copied!

Multiple Bindings of a value

1
iex> x = y = z = 5
2
5
3
iex> x
4
5
5
iex> y
6
5
7
iex> z
8
5