Category Archives: Erlang Thursday

Erlang Thursday – calendar:iso_week_number/1

Today’s Erlang Thursday is on calendar:iso_week_number/1.

calendar:iso_week_number/1 takes a date tuple as an argument, and returns a tuple of the year and week number. The year is the year passed as the date tuple, and the week number is an integer between 1 and 53.

calendar:iso_week_number({2015, 05, 04}).
{2015,19}
calendar:iso_week_number({2015, 05, 03}).
{2015,18}

If we use this week as an example, we can see that a week starts on Monday (the 4th of May), where the Sunday before (May 3rd) was the previous week.

We see that January 1st falls on the first week of the year, no surprise there, and that the 31st of December for 2015, is on the 53rd week of the year.

calendar:iso_week_number({2015, 1, 1}).
{2015,1}
calendar:iso_week_number({2015, 12, 31}).
{2015,53}

Having a 53rd week of the year sounds surprising at first, because everyone talks about 52 weeks in a year, until you realize that sometimes December 31st sometimes falls at the very beginning of a week, causing it to be the 53rd week, since it is only a partial week.

–Proctor

Erlang Thursday – calendar:is_leap_year/1

Today’s Erlang Thursday is on calendar:is_leap_year/1.

calendar:is_leap_year/1 takes a non-negative integer value representing a year, and will return true if that year is a leap year, or false otherwise.

calendar:is_leap_year(2015).
% false
calendar:is_leap_year(2012).
% true
calendar:is_leap_year(2017).
% false
calendar:is_leap_year(2000).
% true
calendar:is_leap_year(1900).
% false
calendar:is_leap_year(0).
% true

By having a built in function as part of the core Erlang libraries, it means you don’t have to code up the rules, or even go lookup the rules to remember how the century years are determined to be leap years or not.

And if you do pass in a negative number for the year, Erlang will raise an exception, as there are no clauses which match a negative number for the year.

calendar:is_leap_year(-1).
% ** exception error: no function clause matching calendar:is_leap_year(-1) (calendar.erl, line 183)
calendar:is_leap_year(-4).
% ** exception error: no function clause matching calendar:is_leap_year(-4) (calendar.erl, line 183)

–Proctor

Erlang Thursday – calendar:valid_date/3

Today’s Erlang Thursday is calendar:valid_date/3.

Originally, I was thinking it was going to be calendar:time_difference/3, but then I looked into the Erlang documentation for the calendar module. and saw that it was marked as obsolete, so today I present calendar:valid_date/3.

The arguments to calendar:valid_date/3 are an integer for the year, integer for the month, and an integer for the day. calendar:valid_date/3 returns the atom true if the day passed in is a valid date, and the atom false if it is not a valid date.

calendar:valid_date(2015, 04, 31).
% false
calendar:valid_date(2015, 04, 30).
% true
calendar:valid_date(2015, 02, 29).
% false
calendar:valid_date(2012, 02, 29).
% true
calendar:valid_date(2015, 11, 31).
% false
calendar:valid_date(2015, 11, 76).
% false
calendar:valid_date(2015, 17, 13).
% false

Just a quick check for our sanity that the day this post was published is a valid date as well.

calendar:valid_date(2015, 04, 23).
% true

Now let’s try to break this a bit and test to see how it can handle 0‘s and negative integer values.

calendar:valid_date(-1, 04, 23).
% false
calendar:valid_date(2015, -7, 21).
% false
calendar:valid_date(2015, 7, -13).
% false
calendar:valid_date(0, 0, 0).
% false

As one might hope, unless you deal with B.C. era dates often, a date with a negative value is not a valid date.

Erlang also provides a calendar:valid_date/1 that takes a tuple of the year, month, and day values as well.

calendar:valid_date({2015, 11, 76}).
% false
calendar:valid_date({2015, 04, 23}).
% true

–Proctor

Erlang Thursday – calendar:date_to_gregorian_days/3

Today’s Erlang Thursday covers calendar:date_to_gregorian_days/3.

As we saw in last week’s Erlang Thursday on calendar:day_of_the_week/3 when we were looking at some error messages, we saw that the errors were coming from calendar:date_to_gregorian_days/3.

calendar:day_of_the_week(0, 0, 0).
% ** exception error: no function clause matching calendar:date_to_gregorian_days(0,0,0) (calendar.erl, line 114)
%      in function  calendar:day_of_the_week/3 (calendar.erl, line 151)
calendar:day_of_the_week(1970, 2, 31).
% ** exception error: no true branch found when evaluating an if expression
%      in function  calendar:date_to_gregorian_days/3 (calendar.erl, line 116)
%      in call from calendar:day_of_the_week/3 (calendar.erl, line 151)
calendar:day_of_the_week(1970, 13, 2).
% ** exception error: no function clause matching calendar:last_day_of_the_month1(1970,13) (calendar.erl, line 243)
%      in function  calendar:date_to_gregorian_days/3 (calendar.erl, line 115)
%      in call from calendar:day_of_the_week/3 (calendar.erl, line 151)

I promised you at the end of that post we would take a deeper look at calendar:date_to_gregorian_days/3 next time, so let’s fulfill that promise.

calendar:date_to_gregorian_days/3 takes three arguments, a non-negative integer for the year, an integer between 1 and 12 (inclusive) for the month, and an integer between 1 and 31 (inclusive) for the day of the month, and returns the number of days since 0000-01-01 in the Gregorian calendar.

calendar:date_to_gregorian_days(2015, 4, 16).
% 736069
calendar:date_to_gregorian_days(0, 1, 1).
% 0
calendar:date_to_gregorian_days(1, 1, 1).
% 366
calendar:date_to_gregorian_days(1970, 1, 1).
% 719528
calendar:date_to_gregorian_days(1999, 12, 31).
% 730484

There is also a version calendar:date_to_gregorian_days/1, that takes a tuple of year, month, and day available if your code already has the date in tuple format.

calendar:date_to_gregorian_days({2015, 4, 16}).
% 736069
calendar:date_to_gregorian_days({0, 1, 1}).
% 0
calendar:date_to_gregorian_days({1, 1, 1}).
% 366

And if we pass something invalid to calendar:date_to_gregorian_days/1, we see that it is calling calendar:date_to_gregorian_days/3. So it is just a nice helper function that does the pattern match destructing for us.

calendar:date_to_gregorian_days({1, 1, 0}).
** exception error: no function clause matching calendar:date_to_gregorian_days(1,1,0) (calendar.erl, line 114)

–Proctor

Erlang Thursday – calendar:day_of_the_week/3

Today’s Erlang Thursday is covers calendar:day_of_the_week/3.

calendar:day_of_the_week/3 allows you to get the day of the week a day occurred on when passed a year, a month and a day.

The first argument represents the year and must be a non-negative integer. The second argument is the month and must be an integer value between 1 and 12 inclusive, representing the 12 months of the Gregorian Calendar, with January being month one. The final argument to calendar:day_of_the_week/3 is the date, and is expected to be a integer between 1 and 31 inclusive.

calendar:day_of_the_week/3 returns an integer value in between 1 and 7 inclusive, with a return value of 1 representing Monday, and a 7 being Sunday.

calendar:day_of_the_week(1, 1, 1).
% 1
calendar:day_of_the_week(1970, 1, 1).
% 4
calendar:day_of_the_week(1999, 12, 31).
% 5
calendar:day_of_the_week(0, 1, 1).
% 6

As you can see that the publish date of this Erlang Thursday post, 2015-04-09, returns a 4, which is in fact a Thursday according to Erlang.

calendar:day_of_the_week(2015, 4, 9).
% 4

There is also a calendar:day_of_the_week/1 function which has all the same constraints but instead of taking three arguments, takes one argument that is a three-tuple of the year, month, and day.

calendar:day_of_the_week({2015, 4, 9}).
% 4
calendar:day_of_the_week({1970, 1, 1}).
% 4
calendar:day_of_the_week({1999, 12, 31}).
% 5

And in the spirit of helping to recognize error messages when we see them, let’s take a look at what we get when we pass some invalid inputs to calendar:day_of_the_week/3.

calendar:day_of_the_week(0, 0, 0).
% ** exception error: no function clause matching calendar:date_to_gregorian_days(0,0,0) (calendar.erl, line 114)
%      in function  calendar:day_of_the_week/3 (calendar.erl, line 151)
calendar:day_of_the_week(1970, 2, 31).
% ** exception error: no true branch found when evaluating an if expression
%      in function  calendar:date_to_gregorian_days/3 (calendar.erl, line 116)
%      in call from calendar:day_of_the_week/3 (calendar.erl, line 151)
calendar:day_of_the_week(1970, 13, 2).
% ** exception error: no function clause matching calendar:last_day_of_the_month1(1970,13) (calendar.erl, line 243)
%      in function  calendar:date_to_gregorian_days/3 (calendar.erl, line 115)
%      in call from calendar:day_of_the_week/3 (calendar.erl, line 151)

If you look at the error messages you see that calendar:day_of_the_week/3 calls calendar:date_to_gregorian_days/3, which we will cover in next week’s Erlang Thursday.

–Proctor

Erlang Thurday – lists:delete/2

Today’s Erlang Thursday in lists:delete/2.

lists:delete/2 takes a Erlang term as it’s first argument, and will remove that item from the list passed in as the second argument.

lists:delete(1, [8, 7, 6, 5, 4, 3, 2, 1]).
% [8,7,6,5,4,3,2]
lists:delete(4, [1, 1, 2, 3, 5, 8]).
% [1,1,2,3,5,8]
lists:delete(72, "Hello World!").
% "ello World!"
lists:delete(d, [a, b, c, d]).
% [a,b,c]
lists:delete(4, []).
% []
lists:delete({b, 2}, [{a, 1}, {b, 2}, {c, 3}]).
% [{a,1},{c,3}]
lists:delete([1, 2, 3], [[4, 5, 6], [7, 8, 9], [1, 2, 3]]).
% [[4,5,6],[7,8,9]]

Note that lists:delete/2 only removes the first item found in the list, and leaves any other occurrences of the item in the list.

€lists:delete(1, [1, 1, 2, 3, 5, 8]).
% [1,2,3,5,8]

As lists:delete/2 was a easy function to demonstrate, and leaving it at this would be a very short post, I thought it might be worth showing a how you might write a very naive1 implementation of lists:delete/2 yourself.

-module(my_lists).

-export([delete/2]).

delete(Item, List) ->
    delete(Item, [], List).

delete(Item, Checked, [Item|Rest]) ->
    lists:reverse(Checked) ++ Rest;
delete(Item, Checked, [Head|Rest]) ->
    delete(Item, [Head | Checked], Rest);
delete(_Item, Checked, []) ->
    lists:reverse(Checked).

Let’s start with our delete function as we expect it to be called from the outside world.

my_lists:delete/2 is the nice API function that just calls delete/3 which is a “private” function (not exported) so the consumer doesn’t have to worry about passing in the accumulator for the items we have checked so far, which we pass as an empty list for the initial value.

-module(my_lists).

-export([delete/2]).

delete(Item, List) ->
    delete(Item, [], List).

The first function clause of delete/3 uses pattern matching to check if the item we want to delete is also the first item in the rest of the list to check. If the pattern match succeeds, we have found the first occurrence of the item to remove! We can stop processing the list and return the result, which is the reverse of the list of items we have checked so far combined with the rest of the items we never got around to checking.

delete(Item, Checked, [Item|Rest]) ->
    lists:reverse(Checked) ++ Rest;

The second clause “knows” that the item we are wanting to delete and the first item in the rest of the list don’t match. How does it “know”? Because if they did match, the first clause would have matched and this clause would not have been evaluated. As we haven’t found the item to remove, we add the item held by Head to the list of Checked items, and then continue calling delete/3. The fact that we are passing a new list of the checked items by prepending Head to the list in Checked is why we need to reverse Checked in the first and third function clauses.

delete(Item, Checked, [Head|Rest]) ->
    delete(Item, [Head | Checked], Rest);

The third, and final, clause of delete/3 has reached the end of the list and not found the item, so we just return the list we have reversed it.

delete(_Item, Checked, []) ->
    lists:reverse(Checked).

There you have it, a very naive1 implementation of your very own version of lists:delete/2.

–Proctor

1. Naive because this is not optimized for performance, or exhaustively tested for completely accurate behavior of lists:delete/2. back

Erlang Thursday – erlang:list_to_atom/1

Today’s Erlang Thursday covers erlang:list_to_atom/1.

erlang:list_to_atom/1 takes a string, and returns an Erlang atom.

list_to_atom("foo").
% foo
list_to_atom("Foo").
% 'Foo'
list_to_atom("Foo_bar").
% 'Foo_bar'
list_to_atom("foo_bar").
% foo_bar
list_to_atom("foo"++"bar").
% foobar
list_to_atom("Erlang").
% 'Erlang'
list_to_atom("Elixir").
% 'Elixir'

This can be useful if you are having to create keys or identifiers based off strings read in from outside the system, such as parsing a CSV style header.

lists:map(fun erlang:list_to_atom/1,
          string:tokens("firstName,lastName,age,gender,preferredName,dateOfBirth", ",")).
% [firstName,lastName,age,gender,preferredName,dateOfBirth]

You do need to be careful when using erlang:list_to_atom/1 on strings acquired from the outside world of your program, as it only handles strings with character values under 256. But any character value1 under 256 is fair game to be turned into an atom.

list_to_atom("Joe, Mike, and Robert").
% 'Joe, Mike, and Robert'
list_to_atom("it's").
% 'it's'
list_to_atom("heyn").
% 'heyn'€
list_to_atom("with_supported_char"++[255]).
% with_supported_charÿ
list_to_atom("with_non_supported_char"++[256]).
% ** exception error: bad argument
%      in function  list_to_atom/1
%         called as list_to_atom([119,105,116,104,95,110,111,110,95,115,117,112,112,111,114,
%                                 116,101,100,95,99,104,97,114,256])

–Proctor


1. Remember that character values are non-negative integer values as well. 0-255 inclusive. back

Erlang Thurday – ordsets:is_disjoint/2

Today’s Erlang Thurday covers ordsets:is_disjoint/2.

There are times in your in your coding day where you have problems where you need to know if given a list of items that none of those items appear in a secondary list.

Your first intuition might be to write out the code as described, like such:

not( lists:any(fun(Item) -> lists:member(Item, [1, 3, 5, 7]) end, [2, 4, 6])).
% true

And while that is accurate, if you redefine your problem in more mathematical terms, you can start to think in sets. When you start to think in terms of sets, you realize that you can check to see if the intersection of the two sets is the empty set.

ordsets:intersection([1, 3, 5, 7], [2, 4, 6]) =:= [].
% true

This is becoming not only more concise, but also more explicit about what you are trying to check.

We can do better still, by checking if the lists are disjoint sets. Enter ordsets:is_disjoint/2.

ordsets:is_disjoint/2 takes two lists, and returns true if no elements are in common.

ordsets:is_disjoint([1, 3, 5, 7], [2, 4, 6]).
% true
ordsets:is_disjoint([1, 2, 3, 5, 7], [2, 4, 6]).
% false

Because ordsets:is_disjoint/2 operates against two lists, we do not have to make sure the elements are unique prior to calling ordsets:disjoint/2.

ordsets:is_disjoint([1, 1, 3, 5, 7, 5, 3], [2, 4, 2, 2, 6]).
% true
€ordsets:is_disjoint([1, 2, 3, 5, 7], [2, 4, 2, 2, 6]).
% false

And if either list passed to ordsets:is_disjoint/2 is an empty list, the result is that the lists are disjoint.

ordsets:is_disjoint([1, 2, 3, 5, 7], []).
% true
ordsets:is_disjoint([], [2, 4, 6]).
% true
ordsets:is_disjoint([], []).
% true

And if you are curious, by running ordsets:is_disjoint/2 through timer:tc/3, we can see that as soon as Erlang knows that the sets are not disjoint, it returns false. And if you remember from the previous Erlang Thrusday on timer:tc/3, the return value is a tuple with the first element being the number of microseconds it took to complete.

timer:tc(ordsets, is_disjoint, [lists:seq(1, 1000000), lists:seq(2000000, 3000000)]).
% {19032,true}
€timer:tc(ordsets, is_disjoint, [lists:seq(1, 1000000), lists:seq(1, 3000000)]).
% {2,false}

–Proctor

Erlang Thursday – lists:flatmap/2

Today’s Erlang Thursday is on lists:flatmap/2.

The selection of this function comes from some Elixir exercises I was doing earlier in the week, and was getting confused because when using the Enum.flat_map/2 function in Elixir wasn’t working as I expected it to.

I circled back to Erlang to check the behavior of lists:flatmap/2 in Erlang, and after getting over my confusion that the flatmap function wasn’t working in Erlang, the common behavior made sense, and I realized I contorted the concept in my head and it was time to take a step back and figure out what lists:flatmap/2 actually does.

Somehow what I had convinced myself that lists:flatmap/2 did was to take a list of items that were nested arbitrarily deep, and mapped over the flattened list in the equivalent of this:

lists:map(fun(X) -> X * X end, lists:flatten([1, [[2, [3]], 4]])).
% [1,4,9,16]

Looking closer at the documentation, after even trying Ruby’s flat_map function, it was clear I had completely confused myself as to how it should work, so time to carefully read the Erlang docs.

They stated that lists:flatmap/2 takes a function that takes an item of type A and returns a list of items that are of type B, and that the second argument to lists:flatmap/2 was a list of items of type A.

Being already confused based off how I thought it should work, it wasn’t registering until I saw the part of the documentation that describes that lists:flatmap/2 behaves as if defined as

flatmap(Fun, List1) ->
    append(map(Fun, List1)).

This started to click for me for what lists:flatmap/2 was actually doing. In the picture that I had in my head, I was expecting to flatten the list first, and then map over the items, but it does the map first, and then does only the flatten, but of only one level deep.

lists:flatmap(fun({Item, Count}) -> 
                  lists:duplicate(Count, Item)
              end,
              [{a, 1}, {b, 2}, {'C', 3}, {'_d_', 4}]).
% [a,b,b,'C','C','C','_d_','_d_','_d_','_d_']

And if we pass those values to the “equivalent” behavior of calling map and then calling append on the list returned from map, we see the results are the same.

lists:append(
    lists:map(fun({Item, Count}) ->
                  lists:duplicate(Count, Item)
              end,
              [{a, 1}, {b, 2}, {'C', 3}, {'_d_', 4}])).
% [a,b,b,'C','C','C','_d_','_d_','_d_','_d_']

And to further clarify, lists:flatmap/2 doesn’t even do a flatten on the resulting list, but simply adjoins the lists that were returned from the mapping function. This can be seen below, as we can see there is still an nested list structure in the results, and the resulting list is not only one level deep.

lists:flatmap(fun(X) -> [X, [X]] end, [a, b, c, d]).
% [a,[a],b,[b],c,[c][/c],d,[d]]

Hope this can save you the confusion I managed to get myself into,
–Proctor

Erlang Thursday – calendar:local_time_to_universal_time_dst/1

In honor of the time change this weekend, today’s Erlang Thursday is on calendar:local_time_to_universal_time_dst/1.

To better understand what calendar:local_time_to_universal_time_dst/1 is doing, it will be contrasted to calendar:local_time_to_universal_time/1.

This coming Sunday is we move the clock forward an hour at 2 AM, so let us see what the time is in UTC right at 1:59 AM.

calendar:local_time_to_universal_time({{2015,3,8},{1,59,59}}).
% {{2015,3,8},{7,59,59}}

Now let’s see what calendar:local_time_to_universal_time/1 returns for 2 AM, which we never actually hit, since the time changes straight to 3 AM.

calendar:local_time_to_universal_time({{2015,3,8},{2,00,00}}).
% {{2015,3,8},{8,0,0}}

But as we dig into the library we see that there is also calendar:local_time_to_universal_time_dst/1. calendar:local_time_to_universal_time_dst/1 returns a list of time tuples, and we start to see why this is important.

This time, let’s call calendar:local_time_to_universal_time_dst/1 with the 2 AM hour and see what is returned.

calendar:local_time_to_universal_time_dst({{2015,3,8},{2,00,00}}).
% []

An empty list!

This makes sense if you take a moment to think about it, as 2 AM doesn’t ever happen so there is no UTC time that it would map to.

And if we look at what happens when we make the jump to 3 AM, we get 8 AM UTC, which is just a second later in UTC, then the time in UTC at 1:59:59 AM.

calendar:local_time_to_universal_time_dst({{2015,3,8},{3,00,00}}).
% [{{2015,3,8},{8,0,0}}]

That covers the case for this coming weekend, so let is check out what is going to happen later this year on November 1st when the clocks move back an hour.

To start with a baseline, let’s see what time it is in UTC at 12:59:59 AM, as we know we only encounter that time once.

calendar:local_time_to_universal_time({{2015,11,1},{0,59,59}}).
% {{2015,11,1},{5,59,59}}
calendar:local_time_to_universal_time_dst({{2015,11,1},{0,59,59}}).
% [{{2015,11,1},{5,59,59}}]

And even though the time change happens a 2 AM, we still only encounter that once, as we move straight to 1 AM the first time.

calendar:local_time_to_universal_time_dst({{2015,11,1},{2,0,0}}).
% [{{2015,11,1},{8,0,0}}]

So at 12:59:59 AM, we are at 5:59:59 AM UTC, and at 2 AM we are at 8 AM UTC. Now comes the tricky part, 1 AM.

1 AM is an odd case, as we will live through 1 AM twice that night. So let’s see what Erlang does with that.

First, we see what time it is in UTC when we call calendar:local_time_to_universal_time with 1 AM.

calendar:local_time_to_universal_time({{2015,11,1},{1,00,00}}).
% {{2015,11,1},{6,0,0}}

We get 6 AM UTC. We move from 5:59:59 AM UTC to 6 AM UTC; and that makes sense, until we start to wonder about the second time we encounter 1 AM.

Now, let’s see what time it is in UTC when we call calendar:local_time_to_universal_time_dst/1 with 1 AM.

calendar:local_time_to_universal_time_dst({{2015,11,1},{1,0,0}}).
% [{{2015,11,1},{6,0,0}},{{2015,11,1},{7,0,0}}]

We get a list with two times in UTC, one for 6 AM UTC when we encounter 1 AM the first time, and one for 7 AM UTC for when we encounter 1 AM the second time around. This happens for every time in the 1 AM hour as well.

calendar:local_time_to_universal_time_dst({{2015,11,1},{1,59,59}}).
% [{{2015,11,1},{6,59,59}},{{2015,11,1},{7,59,59}}]

By returning a list of either no items, one item, or two items, Erlang gives you the most accurate information for you to use, and allow for you to make the informed decision for how you are expecting to handle the times, instead of making some of the decisions for you even if they don’t align with your system’s view of the world.

–Proctor