Core Elixir: IO.Puts

Stand back. We’re going to wrestle some dangerous concepts to the ground, though no processes will actually appear…

The string you’re IO.puts ing can be interpolated. That’s computer science speak for “the language will replace variables with their values inside the quotation marks.”

Like Ruby, Elixir uses #{} for this:

iex> str = 'world'

iex> IO.puts("Hello, #{str}")   # Double quoted strings
Hello, world
:ok

iex> IO.puts('Hello, #{str}')   # Single quoted strings
Hello, world
:ok

In Elixir, the interpolation will happen whether you use single or double quotes. You’ll see I tried it both ways there. The difference in quotation styles is used only in dealing with Elixir strings versus Erlang strings, and we’ll get to that at some point in the nebulous future once the Advil kicks in from getting that terminology right.

You’ll notice that Elixir threw in a new line at the end of the string for us. That’s part of the service IO.puts offers. In other languages, there’s a different command for when you want to use or not use the newline. In Perl, there’s print and say. In Ruby there’s puts and print. In both cases, the print leaves off the newline.

In Elixir, IO.write will not add the newline and print the string as is.

This can look awkward in the iex REPL:

iex> IO.write('Hello, #{str}')
Hello, world:ok

Both IO.puts and IO.write allow you to send your output to different locations, such as standard error. We’ll cover that more in a future article on IO.puts that’ll blow your mind! (When it comes to managing expectations, I’m a failure.)

But, you may ask yourself if you’re bored one day, how does interpolation work?

How Does Interpolation Work?

I’m glad you asked. It’s sorta like polymorphism. Yes, welcome to that rabbit hole…

The first trick is that Elixir sees #{} and rewrites it internally as to_string and then concatenates it with its surroundings.

“Hello, #{str}” becomes “‘Hello, ‘ <> to_string(str)”.

to_string is in the Kernel module (and is thus automatically imported) and adheres to the Strings.Chars protocol.

Any module that follows that protocol must implement a to_string function. Both strings and integers do. More complicated types like tuples do not.

A list will work:

iex> sample_list =['a','b','c','d']
['a', 'b', 'c', 'd']

iex> IO.puts('Hello, #{sample_list}')
Hello, abcd
:ok

A tuple will not:

iex> t = {1, 'a'}
{1, 'a'}

iex> IO.puts('Hello, #{t}')
** (Protocol.UndefinedError) protocol String.Chars not implemented for {1, 'a'}
    (elixir) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir) lib/string/chars.ex:17: String.Chars.to_string/1

In this case, you can work around things by specifying inspect instead of relying on Elixir to use to_string:

iex> IO.puts('Hello, #{inspect t}')
Hello, {1, 'a'}
:ok

Inspect This

That looks a little magical, until you dig a little further and realize how it works:

iex> IO.puts('Hello, #{Kernel.inspect(t)}')
Hello, {1, 'a'}
:ok

inspect, like to_string, is not a magic keyword. It’s a function in the Kernel module. The Kernel module is automatically loaded every time you run Elixir.

If you’re not far enough down the rabbit hole yet, look closer at Kernel.inspect/2. Yes, that’s right: an arity of 2. That second parameter is a list of options that become an Inspect.Opts struct, which is part of the Inspect protocol.

And, yes, we’re going there next.

Inspect.Opts gives you some interesting choices. For example, you can limit the number of values returned. For example:

iex> IO.puts('Hello, #{Kernel.inspect(t, [limit: 1])}')
Hello, {1, ...}
:ok

More drastically:

iex> listicle = ['a', 'b', 'c', 'd', 'e', 'f']
['a', 'b', 'c', 'd', 'e', 'f']
iex> IO.puts('Hello, #{Kernel.inspect(listicle, [limit: 1])}')
Hello, ['a', ...]
:ok

Seems kind of weird to me that the absence of the rest of the values is shown with an ellipses, but I’m sure that comes in handy somewhere.

There are also options like :pretty, which enables pretty printing, and :width that limits the number of characters in a string line when pretty printing is invoked. For example:

iex> IO.puts('Hello, #{Kernel.inspect(listicle, [limit: 3, width: 3])}')
Hello, ['a', 'b', 'c', ...]
:ok
iex> IO.puts('Hello, #{Kernel.inspect(listicle, [pretty: true, limit: 3, width: 3])}')
Hello, ['a',
 'b',
 'c',
 ...]
:ok

You can, of course, see all the parameters in the Inspect.Opts documentation.

OK, let’s work our way back up to the polymorphism I promised earlier.

Polymorphic Protocol

String.chars is a protocol. That is, it creates a set of formulas that anyone following the protocol must create for their particular module if they want to proclaim themselves as conforming to a specific protocol.

In String.chars‘ case, there’s only one function to make happen: to_string(). Whatever structure you’re using, to be sure there’s a way for it to be represented on screen or in a log is for it to hew to this protocol, which is just a convention that has to be uniquely implemented for a given library.

So, multiple modules will have a to_string function, and it’ll work the same everywhere because they conform to the protocol.

With String.chars, you can print the values of certain variable types out to a string, such as in the example we started this whole essay with. IO.puts can only print the value of a variable out to the screen as long as the variable is of a type that can be converted to a string, either through String.chars protocol or Kernel.inspect. The #{} is just shorthand for all of that.

To Sum It Up

IO.puts prints stuff to the screen, and you can include variables in that.

When you interpolate a value into a string, you’re using a couple of Kernel modules, adhering to a protocol, and being all polymorphic.

Thankfully, you didn’t go with the <> operator, instead, because then we could travel down a fun road of macro programming that would take another thousand words to get through.

Completely Unrelated

In my research for this Core Elixir installment, I came across a website with “Hello World” examples in dozens of languages.

The ‘winner’ of the page is a tie between the Whitespace language and the Obfuscated Perl example. Nobody comes close to either of those.

Really, that Perl example will blow your mind.

Scheduling Notice

I’m off to OSCon next week, so Core Elixir will be taking a week off. It’ll be back the following week. Thanks for not weeping openly at this news.

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(7)

Core Elixir: So You Say You Want to Copy a File

To quote Perl, “There’s More Than One Way To Do It.”

In fact, I’m counting six. They come in pairs, though, thanks to our old friend the exclamation point. Let’s pair them off:

File.copy and File.copy!

File.copy has the same exact name and argument order as the Erlang function that does the same thing. Here, look at the source code:

    def copy(source, destination, bytes_count \\ :infinity) do
      F.copy(IO.chardata_to_string(source), IO.chardata_to_string(destination), bytes_count)
    end

(“F” is an alias for the Erlang :file module.)

It literally just converts the strings to the format Erlang prefers and runs the same function.

But what’s with that bytes_count bit at the end? I’ll tell you, but let’s first look at the return values of a successful call to File.copy and File.copy!:

  iex> File.copy('_config.yml', 'testcopyfile_deletemenow.txt')
  {:ok, 3103}

  iex> File.copy!('_config.yml', 'testcopyfile_deletemenow.txt')
  3103

That number that comes back represents the number of bytes that were copied. It is, effectively, the file size.

If you want to pattern match on the results of the copy, then you’ll want to go with cp without the exclamation point at the end.

File.copy has an arity of 3. Yes, there’s a third argument. If you don’t include it, it defaults to :infinity. Otherwise, it acts as a limiter and will only copy over that many bytes of the file to the new one. This is part of the Erlang :file.copy function.

Note: This version of file copying assumes you don’t mind overwriting a file that already exists with the destination’s name. It will do so without warning.

File.cp and File.cp!

I said previously that one of the biggest helps the File module provides in wrapping the Erlang :file module is that it translates Erlang-speak to Unix speak. If you want to copy a file, you can use the cp command with Elixir:

    iex> File.cp('example.txt', 'copy_of_file.txt')
    :ok

And, assuming there are no errors, the cp! function will return the same thing:

    iex> File.cp!('example.txt', 'copy_of_file.txt')
    :ok

As always with the bang, the error message is more human readable with it than without it, unless you’re fluent in POSIX:

    iex> File.cp('file_does_not_exist.txt', 'etc.txt')
    {:error, :enoent}

    iex> File.cp!('file_does_not_exist.txt', 'etc.txt')
    ** (File.CopyError) could not copy recursively from file_does_not_exist.txt to etc.txt: no such file or directory
      (elixir) lib/file.ex:439: File.cp!/3

File.cp_r and File.cp_r!

Things get complicated here.

This is the recursive copy command. If you provide it with a directory as the source, it’ll copy everything in that directory to the directory destination you give it.

It returns the list of files it copied over, which can be extremely handy.

If you give it a single file name, it’ll just copy that single file.

One important note for error checking’s sake here: If the copy fails in the middle, it fails “dirty”. It’ll leave what it had already copied into the source directory. It’s up to you to clean it up.

On success, it’ll return a list of files and directories it has copied. This includes the directory or directories you’re copying into.

iex> File.cp_r('tmp1', 'tmp2')
{:ok, ["tmp2/test3.md", "tmp2/test2.md", "tmp2/test1.md", "tmp2"]}

iex> File.cp_r!('tmp1', 'tmp2')
["tmp2/test3.md", "tmp2/test2.md", "tmp2/test1.md", "tmp2"]

I ran those commands back to back without clearing the second directory. By default, it will just overwrite the files.

You can, however, specify what to do if a file is already found in the destination directory. That’s right, it’s time for another case of The Function with an Extra Arity! File.cp_r is /3. The third parameter is a callback function to handle the source and destination file. If that function returns true, the file will be copied. If false, it won’t be.

Here’s how the function begins in the source code:

 def cp_r(source, destination, callback \\ fn(_, _) -> true end) when is_function(callback) do

Things to notice here:
* There’s a guard clause on there to check that the third parameter is a function.
* If no callback is given, there’s a default function (to match the is_function guard) that gives a default true answer.

So if you want to default to a false answer, then pass a false function. Make something fun up, like

  fn(_, _) -> false end

No, you can do better than that:

  fn(_, _) -> 1 &&& 0 end

Impress your friends! Confuse yourself six months from now with a misplaced sense of humor. Seriously, don’t check that in. You’d be begging for a pull request.

I’ll grab the example given in the documentation and change a couple of things to keep it consistent with what we’ve done so far:

iex(1)> File.cp_r "tmp1", "tmp2", fn(source, destination) ->
...(1)> IO.gets("Overwriting #{destination} by #{source}. Type y to
...(1)> confirm.") == "y"
...(1)> end
Overwriting tmp2/test1.md by tmp1/test1.md. Type y to
confirm.y
Overwriting tmp2/test2.md by tmp1/test2.md. Type y to
confirm.y
Overwriting tmp2/test3.md by tmp1/test3.md. Type y to
confirm.y
{:ok, ["tmp2"]}

It appears that when you fall back to the callback, you lose the list of files you’re copying. If that matters to you, keep this in mind.

More fun: Dig down deep enough in the code and you’ll trip over our old friend, Enum.reduce. Wait, Enum.reduce isn’t your friend yet. I’m writing these Core Elixir posts out of order. We’ll get there; you’ll see. Just remember that name in the meantime…

Out of Context, This Looks Cray-Cray

 defp do_cp_r(_, _, _, acc) do
    acc
  end

Is that Assembly Language? Elixir Morse Code? Elixir Mad Libs?

No, it’s the default pattern match at the end of a list of matches, but its terseness out of context makes it look like utter gibberish. Remember, kids, to always read code in context! (“Code in Context” would make a great PragProg title… Someone else has “Code Complete,” so…)

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(6)

Core Elixir: Home on the Range Module

It seems so small. Two functions and you’re done.

Then you open up the source and see the protocols at work. That’s when you realize that the thing you think of as a range is really a little more complicated than that. A range is not a core Elixir data type. It’s actually just a Range struct:

def new(first, last) do
  %Range{first: first, last: last}
end

It’s a simple and specific struct with all of two values, as you might suspect: the first one and the last one. You get the two dots in the middle for free.

The module also has a test to see if it’s a range:

def range?( %Range{} ), do: true
def range?(_), do: false

It’s almost too obvious, isn’t it? If you pass in an item that pattern matches a Range struct, then you’re a range. Otherwise, you’re not.

Naming wise, it’s a bit different than you might expect. With other data types, Elixir comes pre_loaded with is functions: Does there exist an is_range function? Let’s try it:

iex> r = 1..3
1..3
iex> is_range?(r)
** (RuntimeError) undefined function: is_range?/1

Nope. Let’s take a look. From an iex prompt, type in is_ and hit tab for the auto-complete options:

iex(6)> is_
is_atom/1         is_binary/1       is_bitstring/1    is_boolean/1
is_float/1        is_function/1     is_function/2     is_integer/1
is_list/1         is_map/1          is_nil/1          is_number/1
is_pid/1          is_port/1         is_reference/1    is_tuple/1

Range isn’t on that list. Nor, you may notice is there an is_struct. You need the range? function mentioned above instead:

iex> Range.range?(r)
true

I have to admit that that does look a little weird to me. Could look stranger, though:

iex> range = 1..3
iex> Range.range?(range)
true

This tutorial is showing great range, isn’t it?

The Protocols. Oh, the Protocols!

There are three protocols that Range maps to, and that’s what takes up the second half of the module.

First, the type is enumerable, so it has to define its reduce function to be compatible with everything in the Enum library. When we get to the Enum library in a future “Core Elixir” installment, you’ll see that almost all of the Enum functions are using reduce.

It also implements member? to tell if a value comes inside the range.

def member?(first .. last, value) do
  if first <= last do
    {:ok, first <= value and value <= last}
  else
    {:ok, last <= value and value <= first}
  end
end

That’s fairly simple, too, though Perl6 has it beat:

$low < $value < $high

I’m sure it has to do with the way languages are parsed and Abstract Syntax Trees and all the rest, but why doesn’t every language these days have that? Why do we need a special function or two tests to prove that out?

My kingdom for a macro!

Count Along with Range

Finally, the Range module has a count function, which you can likely guess the purpose of.

Here’s the funny trick there: The count function uses a count function that’s defined as part of the Range.Iterator protocol! Yes, it’s protocols all the way down!

Range.Iterator has count and next defined, for what that might be worth. I won’t bore you too much with those because I bet you’re smart enough to figure out how they work.

Finally, you need to have some way of inspecting the range so, of course, you implement the Inspect protocol!

defimpl Inspect, for: Range do    
  import Inspect.Algebra

  def inspect(first .. last, opts) do
    concat [ to_doc(first, opts), "..", to_doc(last, opts) ]
  end
end

Some background: to_doc is in the Algebra module. I never would have guessed that, but thankfully the Elixir documentation has great search functionality.

The purpose of to_doc is to convert an Elixir structure to an “algebra document.” In other words, it translates a number to a string so it can be printed out. (This is a gross oversimplification, but it’s good enough, I think.)

Long story, short: inspect makes sense, converting the struct to the first number, dot, dot, the last number.

iex> inspect(r)
"1..3"

Hey, nothing in a programming language appears by magic, even something so simple of showing the value of a variable. It all needs to be coded in somewhere.

Your Range Power Tip of the Day

You can have descending ranges! 10..1 is just as valid as 1..10, though obviously your outcome will be wildly different for many functions.

Look through the Range code enough and you’ll find places where that has to be kept in mind as the program runs. The simplest example I can give you is the Range.member?/2 function which, as you might imagine, checks a range to see if a specific value falls within it:

def member?(first .. last, value) do
  if first <= last do
    {:ok, first <= value and value <= last}
  else
    {:ok, last <= value and value <= first}
  end
end

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(5)

Core Elixir: Integer.is_even/1 and Integer.is_odd/1

Is this number odd or even?

This should be simple, right? Oldest trick in the book: If the modulo (remainder) of the number divided by 2 is 0 (has no remainder), then it’s even. Otherwise, it’s odd.

In fact, here, I’ll give you an example with the Elixir rem command, which returns the remainder after dividing the two arguments you send it:

iex> rem(10, 2)
0
iex> rem(11,2)
1

The first one is even. The second one is odd. Done and done. Still simple, right?

Elixir’s rem is actually Kernel.rem and calls out to Erlang’s :erlang.rem to do the actual work.

But that’s not how it works in Elixir. It’s much fancier, and much lower-level.

Back to Bits

Elixir reaches into its back pocket and pulls out a macro on this problem.

I’m not going to get into what a macro is or how it works here. Chris McCord wrote a book about it. Since I haven’t read it yet, it would be irresponsible of me to attempt to break that down any further.

But the trick it uses to determine whether an integer is odd or even is the Bitwise AND operator. I’ll attempt to provide a lesson on that today, instead. It’s likely equally irresponsible of me to attempt this, but I have a Computer Science degree, so we should be good, right?

B to the AND

The Bitwise AND operator converts a number to bits, i.e. it turns it into 1s and 0s. This is ridiculously handy for determining when something is even or odd. Why? That last bit will give it away. If the low bit (the one on the far right side) is turned off (0), the number is even. If it’s turned on (add 1), the number is odd.

Remember, that all the other bits before that last number are even numbers (2, 4, 8, 16, etc.) Only that last one, 1, is odd. And if there’s anything you might remember from second grade math, it’s that even + even is even and even + odd is odd. (Odd + odd is also even, but we don’t have two odd bits to play with.)

So you AND 1 (that is, “00000001”) to the number. 0 AND anything gives you 0, so you effectively zero out all the bits except for maybe that last one.

It comes down to that last bit: 1 AND 1 gives you 1. 0 AND 1 gives you 0. A final value of 1 is odd, 0 is even.

In other words :

So, is_odd returns true when that computation results in 1, and is_even returns true when that computation results in a 0. That’s exactly what the source code does:

defmacro is_odd(n) do
  quote do: (unquote(n) &&& 1) == 1
end

defmacro is_even(n) do
  quote do: (unquote(n) &&& 1) == 0
end

(Even not knowing much about macros, that code is pretty readable.)

One last example of how Binary AND works:

Now you can feel closer to the metal and, like a smarter and more powerful programmer. You just played with the bits!

The Erlang Connection

One last thing I should mention: The &&& operator runs Erlang’s :band function ( B itwise AND, get it?) See the source here.

‘&&&’ is included in Elixir’s Bitwise module, which must be included before you can use it in your modules, as it is in Elixir’s Integer library. Otherwise, you get an error that tells you to do just that:

iex> Bitwise.&&&(20, 1)
** (CompileError) iex:22: you must require Bitwise before invoking the macro Bitwise.&&&/2
    (elixir) src/elixir_dispatch.erl:97: :elixir_dispatch.dispatch_require/6

I only point this out because the error message is so helpful. As I delve deeper into the Elixir core, I see more and more of the most helpful and specific error messages I’ve ever seen in a computer language. That’s handy.

So, use the library and compute away:

iex> use Bitwise
iex> Bitwise.&&&(20, 1)  # 20 is even, so should return 0
0

Note that the return value is the same as if you had done the old divide-by-two trick. No remainder indicates it’s even. And if there is a 1 leftover:

iex> Bitwise.&&&(19, 1) # 19 is odd, so should return 1
1

Boom! Odd.

Including and Requiring

I mentioned before that is_odd and is_even are macros, right? That means that, unlike the Bitwise operator, we can’t just use the Integer module. We need to require or import it. That will ensure that the macros are created and ready to go at compilation time. (The Elixir Getting Started Guide explains this fully, and even uses Integer as its example.)

What’s the difference between the two? With require, you still need to include the module name in your commands:

iex> require Integer
iex> Integer.is_even(20)
true

With import, you don’t need to use the module name anymore:

iex> import Integer
iex> is_odd(20)
false

You also have the ability to only import the specific functions you need, or to include only all of the macros in the module.

iex> import Integer, only: :macros
iex> is_even(20)
true

And Bob’s your uncle.

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(4)

Core Elixir: File.Stat

Last time, we talked about the ‘File.stat’ function.

This time — not to be too confusing — we’re going to talk about the File.Stat library. Notice the capitalization on “Stat”. I know, I have to double check myself every time I write it, too.

The File.Stat library is not something you’ll likely use directly all that much, if ever, but it’s very much necessary for the File.stat command. There are all of two functions in the module, and they’re both meant to translate the stat information between Erlang and Elixir, who use different types to hold the info.

  • File.Stat.to_record will translate the information from an Elixir struct to an Erlang record.

  • File.Stat.from_record will take an Erlang record and give you back an Elixir struct, which you can then match against and have all sorts of fun with.

Let’s start by getting the file information directly from the Erlang :file module:

iex> {:ok, f} = :file.read_file_info("index2.html")
{:ok,
 {:file_info, 3348, :regular, :read_write, { {2015, 5, 15}, {23, 53, 45} },
  { {2011, 7, 7}, {0, 52, 13} }, { {2011, 7, 7}, {0, 52, 15} }, 33188, 1, 16777218,
  0, 2556026, 501, 20} }

You’ll see all the information in there, but it doesn’t include the names as atoms attached next to them. Who knows which one is which? (Yeah, you can look it up, but laziness is a virtue of programming, remember. Thanks, Larry Wall.)

Instead, convert the Erlang file info (in its record form) into an Elixir File struct with File.Stat.from_record/1

iex> f_elixir = File.Stat.from_record(f)
%File.Stat{access: :read_write, atime: { {2015, 5, 15}, {23, 53, 45} },
 ctime: { {2011, 7, 7}, {0, 52, 15}}, gid: 20, inode: 2556026, links: 1,
 major_device: 16777218, minor_device: 0, mode: 33188,
 mtime: { {2011, 7, 7}, {0, 52, 13} }, size: 3348, type: :regular, uid: 501}

Ah, that’s better. We’re back to the Elixir goodness there. When you use the File.stat function, Elixir handles both those steps for you. You end up with the struct. In fact, the only other thing File.stat does for you is convert your Elixir character data into an Erlang string type.

Going the other way: If you want to use an Erlang command to deal with that data, you can transform it back to Erlang’s format:

iex> f_erlang = File.Stat.to_record(f_elixir)
{:file_info, 3348, :regular, :read_write, { {2015, 5, 15}, {23, 53, 45} },
 { {2011, 7, 7}, {0, 52, 13} }, { {2011, 7, 7}, {0, 52, 15} }, 33188, 1, 16777218,
 0, 2556026, 501, 20}

This source code on this is also a classic case where there are twice as many lines to comment the code as there are lines of code. It’s worth it if you need to remind yourself of the definitions of things like inode and ctime. Those Unix terms aren’t always straight forward.

And judging from this discussion on the Elixir-lang mailing list, there’s room for improvement in the documentation here with the data types and whatnot. Take this as a challenge, if you wish…

Core Elixir will return later this week to delve into the world of odds and evens. It’s never as simple as you may think…

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(3)

Core Elixir: File.stat

(Note: Do not confuse the function File.stat with the module File.Stat. Note the capitalization on the second word there. We’ll be covering that one in the near future.)

File.stat, at its heart, is a struct that holds the filesystem information of a given file.

If you have any experience with programming on a Linux or Unix box, a lot of this struct will look very familiar to you. Its functional equivalent is the command line stat command:

[AugieDB] ~ $ stat index.html
16777218 26895214 -rw-r--r-- 1 augiedb staff 0 4437 "May 15 23:53:44 2015" 
"Aug  9 22:47:11 2012" "Aug  9 22:47:11 2012" "Aug  9 22:47:11 2012" 4096 
16 0 index.html

Those values include the file’s permissions, size, the file system id, and more. You can, of course, man stat for further information at the command line.

The Source

In the Elixir world, that command is rewritten as File.stat/2. Other languages have similar things. Perl and Ruby, for example, both have their own File::Stat modules.

Erlang has File.read_file_info in its File library. It populates a data type named file_info with all the information. That’s what Elixir calls on for the sake of File.stat. It then converts it from Erlang’s data structure to a native Elixir struct via File.Stat.from_record/1. (Again, we’ll talk more about that next time.)

In the end, your Elixir request gets a chunk of data like this in response:

 %File.Stat{access: :read_write, atime: { {2015, 5, 15}, {23, 53, 44} },
    ctime: { {2012, 8, 9}, {22, 47, 11 }, gid: 20, inode: 26895214, links: 1,
    major_device: 16777218, minor_device: 0, mode: 33188,
    mtime: { {2012, 8, 9}, {22, 47, 11} }, size: 4437, type: :regular, uid: 501} }

Of the three languages I’ve mentioned in this post, I like this one the best. It just gives you all the results along with the descriptive names in one big shot. You can parse/pattern match and transform all you like from there. No need to remember which values come in which order, or what the naming convention for the new object is. It’s all laid out for you right there in the returned value.

The Options

You can pass along an option list to go along with that file path. You don’t get a lot of options, but they might come in handy for your time-keeping needs:

stat_options :: [{:time, :local | :universal | :posix}]

Basically, you can format the time stamp of the file in whichever way you’d like. Let’s run all the options against the same file and see what the differences are.

We’ll start with the default case, which is local. If you want local, you don’t need to even specify it. (Thus, ‘default’.) Leave the options blank and just pass in the filename. In the interests of your curiosity, though, here’s what it looks like when you include the option:

iex> f = "welcome.html"
iex> File.stat(f, [time: :local])
{:ok,
 %File.Stat{access: :read_write, atime: { {2015, 5, 15}, {23, 53, 45} },
  ctime: { {2014, 2, 16}, {21, 51, 46} }, gid: 20, inode: 50093003, links: 1,
  major_device: 16777218, minor_device: 0, mode: 33188,
  mtime: { {2014, 2, 16}, {21, 51, 46} }, size: 2839, type: :regular, uid: 501} }

The date/time stamp is structured as { {YYYY, MM, DD}, {HH, MM, SS} }.

I’m on the east coast of the United States, so that’s a 9:51:46 p.m. time stamp on the ctime and mtime there.

‘Universal` gives back the Greenwich Prime Meridian time:

iex> File.stat(f, [time: :universal])
{:ok,
 %File.Stat{access: :read_write, atime: { {2015, 5, 16}, {3, 53, 45} },
  ctime: { {2014, 2, 17}, {2, 51, 46} }, gid: 20, inode: 50093003, links: 1,
  major_device: 16777218, minor_device: 0, mode: 33188,
  mtime: { {2014, 2, 17}, {2, 51, 46} }, size: 2839, type: :regular, uid: 501} }

Now you see it’s the following day at 2:51:46 a.m.

And posix gives you back that ridiculously long series of numbers that only a computer could love — or someone doing math with seconds:

iex> File.stat(f, [time: :posix])
{:ok,
 %File.Stat{access: :read_write, atime: 1431748425, ctime: 1392605506, gid: 20,
  inode: 50093003, links: 1, major_device: 16777218, minor_device: 0,
  mode: 33188, mtime: 1392605506, size: 2839, type: :regular, uid: 501}}

That time stamp equates to ‘run it through Google and hope for the best.’ (Here’s one.)

Bang!

There’s a variation on File.stat, too, with File.stat! The difference is that exclamation point at the end. It’s an Elixir convention that a module ending in an exclamation point will not return a tuple, but rather the information you requested, or an error.

For example:

iex> File.stat('File does not exist.md')
{:error, :enoent}

iex> File.stat!('File does not exist.md')
** (File.Error) could not read file stats File does not exist.md: no such file or directory
    (elixir) lib/file.ex:282: File.stat!/2

In the source code, File.stat! calls File.stat to get the job done, then pattern matches on the results to return back the appropriate File.error type if necessary, or just pass back the bare info.

The error codes are taken from POSIX error code standards. Here is a small sampling of those:

  • enoentError No Entry (i.e. no file)
  • eacces – You don’t have the right access rights (permissions) to that file
  • eisdir – Not a file. Might be a directory. Or a device.
  • enotdir – Not a directory
  • enospc – No space. The drive/disk partition is full.
  • eperm – Operation Not Permitted (You can’t do that; it doesn’t make sense.)
  • efbig – Your file is too effing big.

(For more details on the difference between “eperm” and “eacces”, check out this article. Thanks for the link, Jan!)

You can find more errors listed in the Erlang File module documentation. See if you can guess what they all mean, then compare them to this ridiculously exhaustive list that will make your eyes bleed.

And may your bathroom never have an epipe error…

Coming next: We’ll talk about the File.Stat module. It’s different from the File.stat function, I swear.

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(2)

Core Elixir: Introduction and the File Library

Welcome to Core Elixir.

I’ve been busy studying Elixir’s source code lately.

I’ve never looked too deeply into any language’s source code. Perl and Ruby scared me off with their C origins, I guess.

Looking more closely at the Elixir source code, though, I’m Iearning a lot. The code is easy to understand, clearly documented, and not too scary. Granted, I haven’t gone into the parts where stuff is compiled or macros are created or — well, the deeper stuff. I’ll save that for a rainy day. (Monsoon rainy, I should think.)

The outer libraries, though, are fair game.

That’s what “Core Elixir” will be — a series of articles looking at the core libraries of the Elixir language: How they work, what fundamentals of the programming language they expose, where Erlang fits into all of this, how it compares to other languages like Ruby and Perl (because I know something about those two), and more.

And, occasionally, you can watch my frustration or ignorance boil over into something approaching madness and disbelief at the way things work.

The biggest thing I’ve noticed so far is easy to sum up:

Elixir is Elixir

I’m amazed at how much of Elixir is written in Elixir. You can see smaller building blocks being written in Elixir, and then the slightly more complicated ones building themselves on top of those smaller bits. And when Elixir doesn’t have the answer, Erlang does and can be called out to. It’s a remarkably efficient way to create a language that improves on what’s there and doesn’t just copy it as an exercise. If the latter were true, Elixir wouldn’t be nearly as popular as it is today.

I’ve paid the most attention recently to the Elixir File module. As a Perl programmer, being able to manipulate files in various ways is of special interest to me. So much of the work I’ve done in my career has been in automating things that this module does the individual parts of. I can’t help but look here first.

The File Libraries

Elixir’s File library is mostly a wrapper around Erlang’s File module. Or, as we Elixir folks like to call it, :file. The effect is so strong that :file, itself, is aliased at the very top of the Elixir File module to save a lot of typing, like so:

  alias :file, as: F

Seriously, it’s the second line of non-commented code in the module. It’s just that important.

What’s In a Name?

Elixir creator, Jose Valim, has spoken out in that past on the importance of Elixir being about more than just wrapping Erlang functions. So what’s he adding here, specifically? From what I’ve seen, the biggest addition to the language that this module gives us is the ability to think in more Unix-y terms.

Erlang’s file manipulation code is much more florid in its prose. It’s descriptive, but not intuitive to a modern programmer, necessarily. Erlang has function names like get_cwd and set_cwd instead of cwd and cd. Elixir’s stat is Erlang’s read_file_info. It may feel like Erlang’s syntax here is more expressive and plainer in its English, but Elixir utilizes the programmer’s built-in knowledge of the Unix commands. As a programmer, you don’t need to translate Unix to Elixir, or learn a second set of commands to do the same things as the command you already know. The Unix and Elixir commands are identical, for the most part.

Code Pattern

Elixir has private functions, which are defined with a defp. Those, as that description might suggest to you, aren’t available to the programmer using the library.

The File library uses it in an interesting way. The regular function that’s publicly accessible has the simple and memorable name. The work that it does, however, is done in a private function that’s preceded by do_ in its name.

For example, if you want to to create a specific directory, you call on the mkdir_p function with the path name. Elixir will create that directory as well as any of the parent directories along the way. Plain old mkdir will handle creating only a single directory without the option for the parent directories.

Here’s what that function looks like:

def mkdir_p(path) do
  do_mkdir_p(IO.chardata_to_string(path))
end

The only thing it does is call out to a private function of similar name, translating the path name along the way to something Erlang would find more palatable. (We’ll get to the whole string versus list of characters things in a future installment, I’m sure.)

You’ve asked it to run a command, and now Elixir will do_ that command.

From there, Elixir can pattern match to handle the request across multiple versions of the code. In this case, there are two:

  defp do_mkdir_p("/") do
  defp do_mkdir_p(path) do

Spoiler: The first one returns :ok and does nothing, as that directory already exists. The second one actually creates a new directory with the Erlang function :file.make_dir.

Funny enough, in writing this article, I found that this command didn’t use the F alias like every other call to :file did in the program. I submitted a pull request on that over the weekend which got merged in. ( Use file alias ‘F’ for consistency #3412 )

You know how people tend to like short pull requests? That one deleted 5 characters and added 1. This beats my previous shortest pull request by a few characters. Only a few, though…

I <3 open source.

Coming Soon…

File.stat — Or, “inode for Elixir”. Or, File#stat for Elixir. Or, even :file.stat for Elixir.

After that, well, there’s lots of stuff ready to go: Collections and lists, Enumerables, dates and times, odds and evens, why IO.puts is the simplest and most complicated thing ever, where in Elixir There’s More Than One Way To Do It, a diversion into documentation formatting, and so very much more…

If you have any comments, questions, complaints, criticisms, or corrections, catch me on Twitter, @AugieDB. That handle is the same as my GMail account, if you need to type more characters. I want these articles to be factually correct and will update them as necessary.

(1)