IElixir - Elixir kernel for Jupyter Project


Google Summer of Code 2015

Developed by Piotr Przetacznik

Mentored by José Valim


References

Getting Started

Basic Types

1          # integer
0x1F       # integer
1.0        # float
true       # boolean
:atom      # atom / symbol
"elixir"   # string
[1, 2, 3]  # list
{1, 2, 3}  # tuple

Basic arithmetic


In [155]:
1 + 2


Out[155]:
3

In [156]:
5 * 5


Out[156]:
25

In [157]:
10 / 2


Out[157]:
5.0

In [158]:
div(10, 2)


Out[158]:
5

In [159]:
div 10, 2


Out[159]:
5

In [160]:
rem 10, 3


Out[160]:
1

In [161]:
0b1010


Out[161]:
10

In [162]:
0o777


Out[162]:
511

In [163]:
0x1F


Out[163]:
31

In [164]:
1.0


Out[164]:
1.0

In [165]:
1.0e-10


Out[165]:
1.0e-10

In [166]:
round 3.58


Out[166]:
4

In [167]:
trunc 3.58


Out[167]:
3

Booleans


In [168]:
true


Out[168]:
true

In [169]:
true == false


Out[169]:
false

In [170]:
is_boolean(true)


Out[170]:
true

In [171]:
is_boolean(1)


Out[171]:
false

In [172]:
is_integer(5)


Out[172]:
true

In [173]:
is_float(5)


Out[173]:
false

In [174]:
is_number("5.0")


Out[174]:
false

Atoms


In [175]:
:hello


Out[175]:
:hello

In [176]:
:hello == :world


Out[176]:
false

In [177]:
true == :true


Out[177]:
true

In [178]:
is_atom(false)


Out[178]:
true

In [179]:
is_boolean(:false)


Out[179]:
true

Strings


In [180]:
"hellö"


Out[180]:
"hellö"

In [181]:
"hellö #{:world}"


Out[181]:
"hellö world"

In [182]:
IO.puts "hello\nworld"


hello
world
Out[182]:
:ok

In [183]:
is_binary("hellö")


Out[183]:
true

In [184]:
byte_size("hellö")


Out[184]:
6

In [185]:
String.length("hellö")


Out[185]:
5

In [186]:
String.upcase("hellö")


Out[186]:
"HELLÖ"

Anonymous functions


In [187]:
add = fn a, b -> a + b end


Out[187]:
#Function<12.99386804/2 in :erl_eval.expr/5>

In [188]:
is_function(add)


Out[188]:
true

In [189]:
is_function(add, 2)


Out[189]:
true

In [190]:
is_function(add, 1)


Out[190]:
false

In [191]:
add.(1, 2)


Out[191]:
3

In [192]:
add_two = fn a -> add.(a, 2) end


Out[192]:
#Function<6.99386804/1 in :erl_eval.expr/5>

In [193]:
add_two.(2)


Out[193]:
4

In [194]:
x = 42
(fn -> x = 0 end).()
x


warning: variable "x" is unused

Note variables defined inside case, cond, fn, if and similar do not leak. If you want to conditionally override an existing variable "x", you will have to explicitly return the variable. For example:

    if some_condition? do
      atom = :one
    else
      atom = :two
    end

should be written as

    atom =
      if some_condition? do
        :one
      else
        :two
      end

Unused variable found at:
  nofile:2

Out[194]:
42

(Linked) Lists


In [195]:
a = [1, 2, true, 3]


Out[195]:
[1, 2, true, 3]

In [196]:
length [1, 2, 3]


Out[196]:
3

In [197]:
[1, 2, 3] ++ [4, 5, 6]


Out[197]:
[1, 2, 3, 4, 5, 6]

In [198]:
[1, true, 2, false, 3, true] -- [true, false]


Out[198]:
[1, 2, 3, true]

In [199]:
hd(a)


Out[199]:
1

In [200]:
tl(a)


Out[200]:
[2, true, 3]

In [201]:
hd []


** (ArgumentError) "argument error"

In [201]:
[11, 12, 13]


Out[201]:
'\v\f\r'

In [202]:
[104, 101, 108, 108, 111]


Out[202]:
'hello'

In [203]:
'hello' == "hello"


Out[203]:
false

Tuples


In [204]:
{:ok, "hello"}


Out[204]:
{:ok, "hello"}

In [205]:
tuple_size {:ok, "hello"}


Out[205]:
2

In [206]:
tuple = {:ok, "hello"}


Out[206]:
{:ok, "hello"}

In [207]:
elem(tuple, 1)


Out[207]:
"hello"

In [208]:
tuple_size(tuple)


Out[208]:
2

In [209]:
put_elem(tuple, 1, "world")


Out[209]:
{:ok, "world"}

In [210]:
tuple


Out[210]:
{:ok, "hello"}

Lists or tuples?


In [211]:
list = [1|[2|[3|[]]]]


Out[211]:
[1, 2, 3]

In [212]:
[0] ++ list


Out[212]:
[0, 1, 2, 3]

In [213]:
list ++ [4]


Out[213]:
[1, 2, 3, 4]

In [214]:
File.read("LICENSE")


Out[214]:
{:ok, "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-" <> ...}

In [215]:
File.read("path/to/unknown/file")


Out[215]:
{:error, :enoent}

Other examples


In [216]:
0x1F


Out[216]:
31

In [217]:
a = 25
b = 150
IO.puts(a+b)


175
Out[217]:
:ok

In [218]:
defmodule Math do
  def sum(a, b) do
    a + b
  end
end


warning: redefining module Math (current version defined in memory)
  nofile:1

Out[218]:
{:module, Math, <<70, 79, 82, 49, 0, 0, 4, 20, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 124, 0, 0, 0, 14, 11, 69, 108, 105, 120, 105, 114, 46, 77, 97, 116, 104, 8, 95, 95, 105, 110, 102, 111, 95, 95, 7, 99, ...>>, {:sum, 2}}

In [219]:
Math.sum(1, 2)


Out[219]:
3

In [220]:
import ExUnit.CaptureIO
capture_io(fn -> IO.write "john" end) == "john"


Out[220]:
true

In [221]:
?a


Out[221]:
97

In [222]:
<<98>> == <<?b>>


Out[222]:
true

In [223]:
<<?g, ?o, ?\n>> == "go
"


Out[223]:
true

In [224]:
{hlen, blen} = {4, 4}
<<header :: binary-size(hlen), body :: binary-size(blen)>> = "headbody"
{header, body}


Out[224]:
{"head", "body"}

In [225]:
h()


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


In [226]:
defmodule KV.Registry do
  use GenServer

  ## Client API

  @doc """
  Starts the registry.
  """
  def start_link(opts \\ []) do
    GenServer.start_link(__MODULE__, :ok, opts)
  end

  @doc """
  Looks up the bucket pid for `name` stored in `server`.

  Returns `{:ok, pid}` if the bucket exists, `:error` otherwise.
  """
  def lookup(server, name) do
    GenServer.call(server, {:lookup, name})
  end

  @doc """
  Ensures there is a bucket associated to the given `name` in `server`.
  """
  def create(server, name) do
    GenServer.cast(server, {:create, name})
  end

  ## Server Callbacks

  def init(:ok) do
    {:ok, HashDict.new}
  end

  def handle_call({:lookup, name}, _from, names) do
    {:reply, HashDict.fetch(names, name), names}
  end

  def handle_cast({:create, name}, names) do
    if HashDict.has_key?(names, name) do
      {:noreply, names}
    else
      {:ok, bucket} = KV.Bucket.start_link()
      {:noreply, HashDict.put(names, name, bucket)}
    end
  end
end


warning: redefining module KV.Registry (current version defined in memory)
  nofile:1

warning: HashDict.new/0 is deprecated. Use maps and the Map module instead
  nofile:32

warning: HashDict.fetch/2 is deprecated. Use maps and the Map module instead
  nofile:36

warning: HashDict.has_key?/2 is deprecated. Use maps and the Map module instead
  nofile:40

warning: HashDict.put/3 is deprecated. Use maps and the Map module instead
  nofile:44

Out[226]:
{:module, KV.Registry, <<70, 79, 82, 49, 0, 0, 15, 96, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 166, 0, 0, 0, 44, 18, 69, 108, 105, 120, 105, 114, 46, 75, 86, 46, 82, 101, 103, 105, 115, 116, 114, 121, 8, 95, 95, 105, ...>>, {:handle_cast, 2}}

In [227]:
ExUnit.start()


Out[227]:
nil

In [228]:
defmodule KV.RegistryTest do
  use ExUnit.Case, async: true

  setup do
    {:ok, registry} = KV.Registry.start_link
    {:ok, registry: registry}
  end

  test "spawns buckets", %{registry: registry} do
    assert KV.Registry.lookup(registry, "shopping") == :error

    KV.Registry.create(registry, "shopping")
    assert {:ok, bucket} = KV.Registry.lookup(registry, "shopping")

    KV.Bucket.put(bucket, "milk", 1)
    assert KV.Bucket.get(bucket, "milk") == 1
  end
end


warning: redefining module KV.RegistryTest (current version defined in memory)
  nofile:1

Out[228]:
{:module, KV.RegistryTest, <<70, 79, 82, 49, 0, 0, 15, 88, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 232, 0, 0, 0, 47, 22, 69, 108, 105, 120, 105, 114, 46, 75, 86, 46, 82, 101, 103, 105, 115, 116, 114, 121, 84, 101, 115, 116, ...>>, {:"test spawns buckets", 1}}

IElixir magic commands

Get output of previous cell.


In [229]:
ans


Out[229]:
{:module, KV.RegistryTest, <<70, 79, 82, 49, 0, 0, 15, 88, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 1, 232, 0, 0, 0, 47, 22, 69, 108, 105, 120, 105, 114, 46, 75, 86, 46, 82, 101, 103, 105, 115, 116, 114, 121, 84, 101, 115, 116, ...>>, {:"test spawns buckets", 1}}

You can also access output of any cell using it's number.


In [230]:
out[142]


Out[230]:
97