Stratus3D

A blog on software engineering by Trevor Brown

Elixir Cheatsheet

Last year I created an Erlang cheatsheet. I’ve used Erlang for quite a while, so I knew exactly what I wanted on the cheatsheet. I’ve only been using Elixir for about a year so it took me a while to figure out what I wanted to include on an Elixir cheatsheet. But I settled on some things that aren’t that complicated, but I know I’ve forgotten many times. The resulting cheatsheet includes tables on sigils, escape codes, metaprogramming constructs, pattern matching on lists, maps, and structs, and list comprehension syntax. This cheatsheet isn’t intended for beginners because it does not cover any of the fundamentals of the language. Just like the Erlang cheatsheet this cheatsheet is designed to be printed on a single sheet of 8.5 by 11 inch paper.

The cheatsheet is available for you to freely print or download at https://stratus3d.com/elixir-cheatsheet/. The source code has been released under the MIT license and is available on GitHub at Stratus3D/erlang-cheatsheet.

I’d really like to hear your thoughts on the cheatsheet. Is there something you think my cheatsheet is missing? Is there something that can be simplified or removed?

Let me know via email. If you want to contribute directly feel free to create an issue or pull request on the GitHub repository.

Social Media Is Not Important

I’ve been a moderately heavy user of social media on and off for several years now. I’ve been hooked on Facebook, Twitter, and Instagram at different points in time. In recent months I’ve been questioning why I use social media.

Social media is designed to be addictive. Social media sites bombard us with dozens of images, videos, and ads every time we visit them. We’ve become numbed by the constant stimulus. Social media has radically changed how we interact and it has affected society as a whole. I began to question how it has changed me.

I realized that in many ways social media changed social interaction online into a game of sorts for me. The implicit goal in the game was to get likes or shares. I found myself focusing on optimizing my posts for the algorithms, trying to use the right combination of text, photos, and hashtags to generate the most likes and shares. Then I’d constantly check back to see how my latest post was doing. Every time I checked I got a dopamine hit when I saw a new like or share. It had become a game, a game that I was addicted to. I began to question why I participated in the whole thing. Was I really hooked on it? Had I become addicted to the dopamine hit I got from someone liking or sharing one of my posts? Was this the way I want to interact with the world? What was the point of it all?

I realized I didn’t want to live that way.

I wanted more out of social interaction than likes and shares. I wanted lasting relationships, friendships with people who wanted to be more than just Facebook friends, relationships built on shared values and interests, interactions with people who encourage who me to try new things and step out of my comfort zone.

I wanted meaningful, lasting, connections with people.

Social media doesn’t provide that.

Social media isn’t about relationships, it’s about entertainment. A constant bombardment of images, videos, and text designed to entertain us for as long as possible to generate ad revenue. Making meaningful connections with people is not what social media is optimized for.

I realized I had believed the lie that social media was about friendships. In reality social media had inserted itself as the middleman between me and those I cared about. Yes, I have made a few friends online through social media, but most of those friendships were developed outside of social media, many of them in person. I realized there were other lies I had believed that kept me using social media.

Lies I Believed About Social Media

  • It is necessary for building a following

  • It is necessary for maintaining an online presence

  • It is needed to stay in touch with friends and family

These are the lies kept me thinking that I needed social media. The truth is I don’t need social media at all.

Building a Following

Building a following via a blog and a mailing list is far more effective than maintaining a Twitter or Facebook presence. Mailing list subscribers are more valuable than Twitter followers. Since starting the mailing list for this site I’ve gained around 500 subscribers by setting up a list that automatically emails my latest posts out every other month. During that same time I gained around 50 Twitter followers while working hard to post quality tweets and retweeting other relevant content. Getting 500 email subscribers was far easier. This may be the subject of a later blog post.

Maintaining an Online Presence

Maintaining an online presence with a website or blog puts you in control, and allows you to deliver the content you want to deliver to your own subscribers without having to pay the middleman for it.

Keeping Up With Friends and Family

Keeping up with friends and family can be done via phone and email. These means of communication have the added benefit of being more personal. Calling up a family member or friend you’ve not talked to in a couple months is more meaningful than sending them an IM on Facebook. Sharing pictures and video can be done via text message or email. If you want to update a large number of friends and family on what you are doing a mailing list is a good way of sharing updates. Mailchimp makes managing a mailing list easy. Mailing lists have the added benefit of offering more flexibility than a social media site when it comes to composing your update. Email allows you to interleave text and any number photos as you see fit.

In many ways developing friendships the old fashioned ways is easier. Make a special effort to meet those in your neighborhood. Call that friend you haven’t talked to in a year. Find local meetup groups for the things that interest you and attend regularly. Try to get a group of old friends together for an activity. Make a point to facilitate face-to-face social interaction every day.

What I Am Changing

I am working to cut out social media as the middleman in my friendships. I am making an effort to establish more direct means of communication with people I care about - phone, email, etc…​ Am I abandoning social media completely? Right now, not completely. I’ve still got some friends that I only communicate with via Facebook. I am closing my Twitter account and I’m working to wean myself off of Facebook by establishing other means of communication with friends. Eventually I will close my Facebook account.

The people around us are ultimately what make our lives meaningful and make us feel connected. People are important.

Pattern Matching Versus Elixir's Access Behavior

In my last blog post I tried to compare the performance of Elixir’s Access behavior to the Map.get/2 function. While my benchmark did reveal that the Access behavior is a little slower strmpnk on Lobste.rs pointed out a few issues with my benchmarking code. I was generating a new map for every run of the benchmark which put a lot of data on the process heap. The Map.get/2 and the Access behavior both only took just over one microsecond to execute on average so the overhead from all the data in on the heap could easily skew the benchmarks.

New Benchmarks

With the new benchmarking code I only generate a map once and then lookup numerous all the generated keys in the map. Not all keys are present in the map so some lookups will fail. I wanted to test looking up keys that are not present in the map to make it more realistic. Often programs contain conditional logic based on the presence of a key, so I wanted to mirror that in my benchmarking code.

I also updated the code to generate larger maps and lookup more keys. The new code generates maps with 40 keys and tries to lookup 50 different keys. It uses Benchee for the benchmarks. The new benchmarking code looks like this (thanks to strmpnk for this code):

num_map_keys = 40
num_keys_total = 50

keys =
  1..num_keys_total
  |> Enum.map(fn key ->
    Integer.to_string(key)
  end)

map =
  keys
  |> Enum.take_random(num_map_keys)
  |> Map.new(&{&1, true})

num_times = 1..100_000

Benchee.run(%{
  # Compare an empty loop as a baseline
  "baseline" => fn ->
    for _key <- keys, _ <- num_times, do: :ok
    :ok
  end,
  "Map.get" => fn ->
    for key <- keys, _ <- num_times do
      Map.get(map, key)
    end

    :ok
  end,
  "Access" => fn ->
    for key <- keys, _ <- num_times do
      map[key]
    end

    :ok
  end
})

All these functions return :ok instead of the list generated by the for loop. This is to reduce the impact of the list generated by the for loop as it can be garbage collected as soon as the loop finishes. I also have a baseline function that contains the same for loop as the functions, but without the code to lookup values in the map. This allows me to measure the overhead of the for loop and other code in the benchmark functions here.

Pattern Matching

In my last blog post I didn’t consider another option for getting a value out of a map - pattern matching. It’s easy enough to pattern match a value out of a map when you know the key, and it’s the only way to bind a variable to a value in a function head. It’s also available in Erlang and Elixir, unlike the Access behavior and the Map.get/2 function. The only difference is that with pattern matching the map will not match if the key is not found, so we have to use the pattern as a clause in a case statement if we do not want to raise an exception:

  "Pattern Matching" => fn ->
    for key <- keys, _ <- num_times do
      case map do
        %{^key => value} ->
          value

        _ ->
          false
      end
    end

    :ok
  end

Results

Name                       ips        average  deviation         median         99th %
baseline                  8.54      117.08 ms     ±4.28%      115.46 ms      143.10 ms
Pattern Matching          7.72      129.50 ms     ±8.66%      125.02 ms      164.67 ms
Map.get                   2.32      431.59 ms     ±5.07%      424.35 ms      500.32 ms
Access                    1.32      757.32 ms    ±38.46%      565.16 ms     1266.41 ms

Comparison:
baseline                  8.54
Pattern Matching          7.72 - 1.11x slower +12.41 ms
Map.get                   2.32 - 3.69x slower +314.50 ms
Access                    1.32 - 6.47x slower +640.23 ms

Pattern matching was only slightly slower than the baseline benchmark. It was about 3 times faster than Map.get/2 and about 6 times faster than the Access behavior. I expected pattern matching to be faster than the Access behavior, but not expect it to be so much faster than the Map.get/2 function. It’s not immediately clear why it’s so much faster. After all, the Map.get/2 implementation is almost exactly the same as the code I wrote above for the benchmark:

# Map.get/2 implementation

@spec get(map, key, value) :: value
def get(map, key, default \\ nil) do
  case map do
    %{^key => value} ->
      value

    %{} ->
      default

    other ->
      :erlang.error({:badmap, other}, [map, key, default])
  end
end

My theory is the compiler is combining the case statement and the anonymous function in the benchmark code during an optimization pass to reduce the number of reductions needed to execute the code. It’s possible the compiler optimized out the pattern matching code completely, since the value it retrieves is never used. Perhaps I’ll investigate this in a later blog post.

Conclusion

If you are concerned about performance use pattern matching. Pattern matching is one of the core features of the Erlang VM and it’s used extensively in the standard library. In this case Map.get/2 and the Access behavior are using pattern matching under the hood so for whichever method you choose pattern matching will be used to retrieve the value you want to lookup. If pattern matching isn’t easy to do in the context in which you need the value, Map.get/2 performs better than square bracket Access syntax, but is still slower than doing pattern matching directly.

Whether the performance of code used to retrieve a key from a map matters at all in practice is another discussion. In most cases it’s not even something you should think about.