Learn With Me: Elixir - IEx and Code Comments (#12)

In this post, I'm going to cover a few miscellaneous topics that did't fit very well into what I've covered so far.

IEx Commands

IEx has its own set of commands, and I'm going to cover some of the useful ones I've learned so far. IEx commands are actually Elixir functions contained in the IEx.Helpers module, but from within the IEx shell, they just look like regular shell commands.

The h command

The h command an abbreviation of "help", and you can use it to pull up information on almost anything in Elixir (or at least that how it appears to me). I suspect that compiled Elixir modules and functions contain some sort of documentation metadata, which the h command retrieves and displays. That's purely speculation on my part, but it would not surprise me that once we learn how to attach documentation to code, the h command would display documentation for our own code as well.

Since the h command is a function, you can call it like so: h([something]). However, since this IEx is a shell, I prefer to use the space syntax instead of the parentheses syntax: h [something]. Let's look at an example.

We can get the documentation associated with a module, in this case the String module.

iex> h String

                                     String

A String in Elixir is a UTF-8 encoded binary.

## Codepoints and grapheme cluster

The functions in this module act according to the Unicode Standard, version
11.0.0.

As per the standard, a codepoint is a single Unicode Character, which may be
represented by one or more bytes.

For example, the codepoint "é" is two bytes:

    iex> byte_size("é")
    2

However, this module returns the proper length:

    iex> String.length("é")
    1

Furthermore, this module also presents the concept of grapheme cluster (from
now on referenced as graphemes). Graphemes can consist of multiple codepoints
that may be perceived as a single character by readers. For example, "é" can be

...

I'm only including part of the results in the above example. The text continues for quite a while.

We can also retrieve documentation for a particular function. The following retrieves the documentation for String.upcase/1.

iex> h String.upcase/1

                      def upcase(string, mode \\ :default)

Converts all characters in the given string to uppercase according to mode.

mode may be :default, :ascii or :greek. The :default mode considers all
non-conditional transformations outlined in the Unicode standard. :ascii
uppercases only the letters a to z. :greek includes the context sensitive
mappings found in Greek.

## Examples

    iex> String.upcase("abcd")
    "ABCD"

    iex> String.upcase("ab 123 xpto")
    "AB 123 XPTO"

    iex> String.upcase("olá")
    "OLÁ"

The :ascii mode ignores Unicode characters and provides a more performant
implementation when you know the string contains only ASCII characters:

    iex> String.upcase("olá", :ascii)
    "OLá"

You can even use h to get the documentation for itself by typing h h.

All the documentation that you can retrieve for anything in the official Elixir environment appears to be also available on the Elixir website. However, the h command is useful if you don't want to have to go search for that documentaton. I imagine that it is also useful for summoning documentation for other Elixir frameworks and libaries.

The c command

The c command loads and compiles one or more modules from source code files, making the loaded modules available for use in the IEx shell.

I already showed how to use this command in the second post covering functions and modules. I'll give a brief example here.

iex> c "math_operations.exs"
[MathOperations]

The above code example loads the MathOperations module from math_operations.exs. After that, we can call the functions in that module from within IEx.

The i command

The i command prints data type information. You can use this command to look up information about a variable, function, module, or pretty much anything that represents some kind of data. Since Elixir is a dynamically-typed language, this is most useful when you see some kind of identifier and you want to find out more about it. The i command will tell you if it's a function, string, atom, list, or whatever else.

This example prints out information regarding the String module. There's quite a lot of information available. It's interesting to see that the data type of a module identifier is actually an atom. It even tells me where the source code was located on José's hard drive before it was compiled. :)

iex> i String
Term
  String
Data type
  Atom
Module bytecode
  c:/Program Files (x86)/Elixir/lib/elixir/ebin/Elixir.String.beam
Source
  /Users/jose/OSS/elixir/lib/elixir/lib/string.ex
Version
  [120667546776156801928804664897518077413]
Compile options
  [:debug_info]
Description
  Use h(String) to access its documentation.
  Call String.module_info() to access metadata.
Raw representation
  :"Elixir.String"
Reference modules
  Module, Atom
Implemented protocols
  IEx.Info, Inspect, List.Chars, String.Chars

The next examples print out information regarding a string, integer, and a list.

iex> name = "Bob"
"Bob"
iex> i name
Term
  "Bob"
Data type
  BitString
Byte size
  3
Description
  This is a string: a UTF-8 encoded binary. It's printed surrounded by
  "double quotes" because all UTF-8 encoded codepoints in it are printable.
Raw representation
  <<66, 111, 98>>
Reference modules
  String, :binary
Implemented protocols
  IEx.Info, Collectable, Inspect, List.Chars, String.Chars
iex> age = 32
32
iex> i age
Term
  32
Data type
  Integer
Reference modules
  Integer
Implemented protocols
  IEx.Info, Inspect, List.Chars, String.Chars
iex> i [1, 2, 3]
Term
  [1, 2, 3]
Data type
  List
Reference modules
  List
Implemented protocols
  IEx.Info, Collectable, Enumerable, Inspect, List.Chars, String.Chars

That's interesting. I don't know what protocols are yet, but based on the information I see here, they sure appear to resemble interfaces in C#. I'll bet that they are at least something similar to interfaces.

I don't yet understand the meaning of all this information yet, but I'll bet this will be really useful once I become a proficient Elixir developer.

Other IEx commands

There are many more commands in IEx, most of which I've never used. You can look at a summary of them by typing h IEx.Helpers.

iex> h IEx.Helpers

                                  IEx.Helpers

Welcome to Interactive Elixir. You are currently seeing the documentation for
the module IEx.Helpers which provides many helpers to make Elixir's shell more
joyful to work with.

This message was triggered by invoking the helper h(), usually referred to as
h/0 (since it expects 0 arguments).

You can use the h/1 function to invoke the documentation for any Elixir module
or function:

    iex> h(Enum)
    iex> h(Enum.map)
    iex> h(Enum.reverse/1)

You can also use the i/1 function to introspect any value you have in the
shell:

    iex> i("hello")

There are many other helpers available, here are some examples:

  ΓÇó b/1            - prints callbacks info and docs for a given module
  ΓÇó c/1            - compiles a file
  ΓÇó c/2            - compiles a file and writes bytecode to the given path
  ΓÇó cd/1           - changes the current directory
  ΓÇó clear/0        - clears the screen
  ΓÇó exports/1      - shows all exports (functions + macros) in a module
  ΓÇó flush/0        - flushes all messages sent to the shell
  ΓÇó h/0            - prints this help message
  ΓÇó h/1            - prints help for the given module, function or macro
  ΓÇó i/0            - prints information about the last value
  ΓÇó i/1            - prints information about the given term
  ΓÇó ls/0           - lists the contents of the current directory
  ΓÇó ls/1           - lists the contents of the specified directory
  ΓÇó open/1         - opens the source for the given module or function in
    your editor
  ΓÇó pid/1          - creates a PID from a string
  ΓÇó pid/3          - creates a PID with the 3 integer arguments passed
  ΓÇó ref/1          - creates a Reference from a string
  ΓÇó ref/4          - creates a Reference with the 4 integer arguments
    passed
  ΓÇó pwd/0          - prints the current working directory
  ΓÇó r/1            - recompiles the given module's source file
  ΓÇó recompile/0    - recompiles the current project
  ΓÇó runtime_info/0 - prints runtime info (versions, memory usage, stats)
  ΓÇó v/0            - retrieves the last value from the history
  ΓÇó v/1            - retrieves the nth value from the history

Help for all of those functions can be consulted directly from the command line
using the h/1 helper itself. Try:

    iex> h(v/0)

To list all IEx helpers available, which is effectively all exports (functions
and macros) in the IEx.Helpers module:

    iex> exports(IEx.Helpers)

This module also includes helpers for debugging purposes, see IEx.break!/4 for
more information.

To learn more about IEx as a whole, type h(IEx).

Typing h IEx will give you a lot of documentation about IEx and how it works. I have not yet read all of it, but it's quite informative. The same IEx documentation is available on the Elixir website. It includes the module documentation and the documentation for the functions within the module. No doubt it all comes from the same source.

IEx Tips

Here's a tip that that has helped me out when in IEx. Sometime you mess up the syntax and IEx wants you to type in the magic characters to complete the statement. There are times when you just want to abort the statement completely, but the only way to do that is to figure out the characters that Elixir wants or exist IEx entirely by pressing Ctrl+C twice.

However, it is possible to just abort the statement by typing in #iex:break. I have no idea what the significance of that is, but it allows you to abort the statement and get back to the IEx prompt.

iex> list = [1, 2
...>
...>
...> 
...> #iex:break
** (TokenMissingError) iex:5: incomplete expression

iex>

Another tip is to use the v function in Elixir. That will repeat the last thing you entered so you don't have to type it all over again. This is particularly helpful if the last statement was really long and you don't want to have to type it all over again.

iex> [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
iex> v
[1, 2, 3, 4, 5]
iex> list = v()
[1, 2, 3, 4, 5]
iex> list
[1, 2, 3, 4, 5]
iex>

Running Code Directly

From what I understand, we don't have to go into IEx to run a code file. We can run code directly from the command line.

So I created the following code in the ElixirOperations project that I had created as part of writing the last post. You can find the ElixirOperator project in its own folder in the Learn With Me: Elixir repository on Github.

Operator.apply(&MathOperations.add/2, 4, 9)
Operator.apply(&MathOperations.subtract/2, 4, 9)
Operator.apply(&MathOperations.multiply/2, 4, 9)
Operator.apply(&MathOperations.divide/2, 4, 9)
Operator.apply(&MathOperations.mod/2, 9, 4)
Operator.apply(&MathOperations.negate/1, 9)
Operator.apply(&MathOperations.square/1, 9)
Operator.apply(&MathOperations.square/1, MathOperations.pi)

Then I tried running it using elixir perform_operations.exs. That resulted in the following error:

** (UndefinedFunctionError) function Operator.apply/3 is undefined (module Operator is not available)
    Operator.apply(&MathOperations.add/2, 4, 9)
    perform_operations.exs:1: (file)
    (elixir) lib/code.ex:767: Code.require_file/2

So it clearly couldn't locate and load other modules that way. I then tried passing all the required files in as parameters.

elixir operator.exs math_operations.exs perform_operations.exs

There was absolutely no output. OK, it's not IEx, so I probably need to output those values to the screen. So then I tried adding a calls to do so.

IO.puts("Hello")

IO.puts Operator.apply(&MathOperations.add/2, 4, 9)
IO.puts Operator.apply(&MathOperations.subtract/2, 4, 9)
IO.puts Operator.apply(&MathOperations.multiply/2, 4, 9)
IO.puts Operator.apply(&MathOperations.divide/2, 4, 9)
IO.puts Operator.apply(&MathOperations.mod/2, 9, 4)
IO.puts Operator.apply(&MathOperations.negate/1, 9)
IO.puts Operator.apply(&MathOperations.square/1, 9)
IO.puts Operator.apply(&MathOperations.square/1, MathOperations.pi)

There was still no output. By reading the elixir tool help documentation (just type elixir --help), I found that it was only running the first file and the rest were being regarded as data. Perhaps they are being used as parameters for the script?

Anyway, I then discovered that the other files could be loaded using the -r switch, like so:

elixir -r operator.exs -r math_operations.exs perform_operations.exs
Hello
13
-5
36
0.4444444444444444
1
-9
81
9.869604401089358

Very nice. I can now run .exs files as scripts from outside IEx.

Comments

Single-line comments in Elixir are preceded by the # character. This is equivalent to the // comment in C# and Javascript.

#This is a code comment
name = "Bob"

#Convert the name to upper-case
name = String.upcase(name)

There is no generic multi-line comment syntax similar to /* */ in C# and Javascript. From what I understand there is a special syntax that allows multi-line documentation of functions and modules, but that's meant for standardized documentation only, not comments in the middle of a function.

Seeing how documentation metadata can be retrieved in IEx, I think it's quite likely that there is some special documentation format that needs to be followed. I have no idea what that looks like yet. So until then, I'll just document my functions with standard comments.

Comments can be used in IEx, and they evaluate to nil. I know because I tried. However, comments are more useful in source code files than they are in IEx.

iex> #This is a comment
nil
iex> #This is another comment
nil
iex> name = "Bob"
"Bob"

By the way, I haven't actually covered nil yet, and that's because I know nothing about it. I think it's safe to assume that it's similar to null in C# or Javascript, but I have no sense of how it is used in Elixir. I'm sure I'll learn about how it's typically used as I go along.