I am using the following development stack at the moment of writing this article.
Erlang/OTP 22 [erts-10.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]Elixir 1.9.1 (compiled with Erlang/OTP 20)The title of this tip may look odd, but it makes sense when you see the requirement. I just developed a fake story.
I have a float number 3.4 and a friend of mine asked to convert that to a binary .
I said that it is so simple just use to_string function from Kernel module.
float = 3.4
to_string float
"3.4"Again, he asked me to not use to_string then I have shown him this
float = 3.4
float_string = inspect(float)
"3.4"He said that he don’t want to use any function then I have shown him this
float = 3.4
float_string = "#{float}"He is still not convinced. He needs two decimals after a period. It’s like "3.40"
After thinking a while, I revisited modules in Elixir. I found Float.to_string but it is not going to solve our problem.
The Erlang is the place where you find more hidden treasures. If you need to convert a float to a string with an explicit decimal precision, use the built-in Erlang function float_to_binary
iex> :erlang.float_to_binary 3.4, decimals: 2
"3.40"
iex> :erlang.float_to_binary 3.4, decimals: 5
"3.40000"
NOTE
The decimals option should be with in the range 0-253
We can limit the keys to print while we inspect structs using @derive attribute.
Don’t believe, check it down
defmodule Address do
@derive {Inspect, only: [:name, :country]}
defstruct [name: "john", street: "2nd lane", door_no: "12-3", state: "mystate", country: "My Country" ]
end
iex> %Address{}
#Address<country: "My Country", name: "john", ...> #OUTPUTLet’s consider we have a requirement to modify a map and need to merge the modified map with some other map. API developers always face this situation like modifying the response according to the client.
Consider the following two maps user and location
user = %{name: "blackode", publication: "medium", age: 25}
location = %{latitude: 38.8951, longitude: -77.0364}Now, we have a requirement to modify the keys for location map from latitude to lat and longitude to long. After map modification, the modified map has to merge into user map.
Just to convey the idea, I am using a map with fewer number of key-value pairs.
Before, I was doing Enum.map/2 followed by Enum.into/2 followed by Map.merge/2
location
|> Enum.map(fn {:latitude, lat} -> {:lat, lat}
{:longitude, long} -> {:long, long}
end)
|> Enum.into(%{}) #as map gives keyword list
|> Map.merge(user)We can simply achieve this using alone Enum.into/3
Enum.into(location, user, fn
{:latitude, lat} -> {:lat, lat}
{:longitude, long} -> {:long, long}
end)System.schedulersYou can also check online schedulers like
System.schedulers_onlineYou can type in your terminal not iex print the number of processing units available using the command nproc
$ nprocWe can know queue length of a process using Process.info/2
Process.info(pid, :message_queue_len)Let’s check that
iex> send self, :hello
iex> send self, :hiAbove lines will send two messages to the current process. As we did not write any receive block here, they keep waiting inside mailbox queue.
Now we will check the messages queue length of the current process self
iex> Process.info(self, :message_queue_len)
{:message_queue_len, 2}Now we handle one message using receive block and will check the queue length once again.
iex> receive do: (:hello -> "I GOT HELLO")
"I GOT HELLO"
iex> Process.info(self, :message_queue_len)
{:message_queue_len, 1}Did you see that, we have only one message in queue as we handled :hello message.
Again, we handle the left over message :hi and this time the length will be 0 Of course it will be as there are no more messages to handle.
iex> receive do: (:hi -> "I GOT HI")
"I GOT HI"
iex> Process.info(self, :message_queue_len)
{:message_queue_len, 0}https://asciinema.org/a/271142?source=post_page-----7155532befd7----------------------
We always try to execute the project module functions in iex interactive shell to check its behavior.
Sometimes, our module names will be lengthy to type. Of course, we can type alias in iex but it vanishes every time you restart the iex shell and you have to type the aliases once again.
Create .iex.exs file in the project root directory and add all aliases to the file.
Whenever you start iex shell, it looks for .iex.exs file in the current working folder . So, it creates all the aliases in the .iex.exs file.
If file isn’t exist in the current directory, then it looks in your home directory i.e ~/.iex.exs.
Requirement 3.4 to “3”
To meet our needs, we need to convert the float to integer using trunc function from Kernel module and then passing it to the to_string function.
Initially, I was doing like in the following which I don’t recommend you to do.
float = 3.4
float
|> Kernel.trunc()
|> to_string()It is too much typing and have to use extra conversion here.
We can achieve this using :erlang.float_to_binary passing decimals option.
:erlang.float_to_binary(3.4, decimals: 0)
"3"mix test --slowest N # N is integerexamplemix test --slowest 3Replace N with any integer value then it prints timing information for the N slowest tests.
It automatically adds--trace and--preload-modules
It is simply implementing Inspect protocol for our custom type.
We can re design the inspect output for any specific type by implementing the Inspect protocol and overriding inspect function.
defmodule Student do
defstruct name: "John", place: "Earth"
end
defimpl Inspect, for: Student do
def inspect(student, _opts) do
"""
-----------|---------------------
Name : #{student.name}
-----------|---------------------
Place : #{student.place}
-----------|---------------------
"""
end
end
iex> %Student{}
-----------|---------------------
Name : John
-----------|---------------------
Place : Earth
-----------|---------------------It is highly useful for custom structs where we can define what we need to see whenever we inspect our struct.
It is trivial that we need a temporary directory to write certain files and to delete later based on our code logic.
In elixir, we can get the path based on the operating system the app is running using System.tmp_dir/0 which returns a writable temporary directory.
We can customize the path with different environment variables like in the following;
export TMP="~/tmp"
iex
iex> System.tmp_dirIt returns the directory set to TMP environment variable if available
export TMPDIR="~/temp"
iex
iex> System.tmp_dirIt returns the directory set to TMPDIR environment variable if available
If three environment variables are set, then it searches for directories in the following order
1. TMPDIR
2. TEMP
3. TMP
If none of them was found then it returns default directories based on the operating system like C:\TMP on Windows or /tmpon Unix
In case the default directories are not found then it returns PWD i.e present working directory as a last piece of cake.
Though you exported Environmental variables, it still gives default values if those exported directories are not available.






