It is a great pleasure to come up with another set of tips that you may or many not know. I am using 👇

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)

1. Float to binary — Required Decimal Precision

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"

🔥 The decimals option should be with in the range 0-253

2. Inspecting Only Derived Elements in Structs

we can limit the keys to print while we inspect structs using @derive attribute. Don’t believe check 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

IO.inspect Address%{}
#Address<country: "My Country", name: "john", ...>  #OUTPUT

3. Enumeration & Merging two maps

Let’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

#Don’t Do

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

#Do

Enum.into(location, user, fn
  {:latitude, lat} -> {:lat, lat}
  {:longitude, long} -> {:long, long}
end)

4. Finding System Cpu’s available count

System.schedulers

You can also check online schedulers like

System.schedulers_online

You can type in your terminal not iex print the number of processing units available using the command nproc

$ nproc

5. Finding Message Queue Length of a Process

We can know queue length of a process using Process.info/2

Process.info(pid, :message_que_len)

Let’s check that

iex> send self, :hello
iex> send self, :hi

Above 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_que_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: (:hello -> "I GOT HI")
"I GOT HI"

iex> Process.info(self, :message_queue_len)
{:message_queue_len, 0}

6. Loading project Module aliases (.iex.exs)

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.

7. Float to a binary with out precision

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.

#Don’t 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.

#Do

:erlang.float_to_binary(3.4, decimals: 0)
"3"

8. Finding N slowest tests in mix application

mix test --slowest N  # N is integer

example

mix test --slowest 3


Replace N with any integer value then it prints timing information for the N slowest tests.

It automatically adds--trace and--preload-modules

9. Re-Designing Custom data type inspection across the app

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.

10. Writable Temporary Directory

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_dir

It returns the directory set to TMP environment variable if available

export TEMP="~/temp"
iex
iex> System.tmp_dir

It returns the directory set to TEMP environment variable if available

export TMPDIR="~/temp"
iex
iex> System.tmp_dir

It 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.

WARNING

Though you exported Environmental variables, it still gives default values if those exported directories are not available.


Join Our Telegram Channel

Blackoders
  Telegram
  Channel

Check out the GitHub repository on Killer Elixir Tips

Blackoders
  Telegram
  Channel Glad if you can contribute with a

🎉 Happy Coding :)


author image
WRITTEN BY
Blackode