This talk was give for Elixir Montreal Meetup to talk about how the elixir formatter works and how it compiles the source code to generate a pretty format
2. About Me
- 10 years of experience in programming
- Computer Scientist @ Universidade
Católica de Brasília
- Currently @ Shopify
- /pedrosnk
- /pesnk
- Has Deployed an Elixir App Before Phoenix
Fun Fact
4. State of Elixir 1.5
• Shipped features to help
developers and
documentation
• Breakpoints
• Exception Blame
• @impl
5. breakpoint
• A set of functions available on iex to help debug
• break!/2, break!/4, breaks/0, continue/0, open/0,
remove_breaks/0, remove_breaks/1, reset_break/1,
reset_break/3, respawn/0, whereiam/1
7. Exception Blame
iex(1)> Access.fetch :foo, :bar
** (FunctionClauseError) no function clause matching in Access.fetch/2
The following arguments were given to Access.fetch/2:
# 1
:foo
# 2
:bar
Attempted function clauses (showing 5 out of 5):
def fetch(%struct{} = container, key)
def fetch(map, key) when is_map(map)
def fetch(list, key) when is_list(list) and is_atom(key)
def fetch(list, key) when is_list(list)
def fetch(nil, _key)
(elixir) lib/access.ex:261: Access.fetch/2
8. @impl
Add ability to mark which function
in a module is a implementation of
a call back.
defmodule Stack do
use GenServer
@impl GenServer
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
@impl GenServer
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
end
defmodule MyApp do
@behaviour Plug
@impl Plug
def init(_opts) do
opts
end
@impl Plug
def call(conn, _opts) do
Plug.Conn.send_resp(conn, 200, "hello world")
end
end
12. Formatter
• Formats your code to maintain a consistency style for all
elixir projects
• Community code standards and a common guide for
newcomers
• Maintain the same AST as the original code. Formatter is
not a Linter
• Built in inside mix
13. Run the formatter
defmodule MessedCode do
use GenServer
@default_list [
1,2,3,
4,5
]
def say_hello first_name, last_name do
"Hello #{first_name} #{last_name}"
end
@impl GenServer
def handle_call { :add_to_list, value}, _from,%{values: list,stage: stage }=_state do
stage = case stage do
"initial" -> "processing"
"progress" -> "final"
"final" -> "final"
_unkown -> "unkown"
end
{:reply, :ok,
%{value: list ++ [value],
stage: stage}}
end
end
17. What happened?
defmodule Fac do
@spec fac(pos_integer) :: pos_integer
@doc "Compute factorial"
def fac 1 do; 1 ;end # stop condition
def fac n do
n * fac n - 1
end
end
18. What happened?> raw = File.read!("lib/fac.ex")
> formated = Code.format_string!(raw)
["defmodule", " ", "Fac", " do", "n ", "@spec", " ", "fac", "(", "",
"pos_integer", "", ")", " ::", " ", "pos_integer", "n ", "@doc", " ", """,
"Compute factorial", """, "n ", "# stop condition", "n ", …]
> formated |> Enum.join() |> IO.puts()
defmodule Fac do
@spec fac(pos_integer) :: pos_integer
@doc "Compute factorial"
# stop condition
def fac(1) do
1
end
def fac(n) do
n * fac(n - 1)
end
end
31. Deeper inside
Code.format_string!
defmodule Fac do
@spec fac(pos_integer) :: pos_integer
@doc "Compute factorial"
# stop condition
def fac(1) do
1
end
def fac(n) do
n * fac(n - 1)
end
end
defmodule Fac do
@spec fac(pos_integer) :: pos_integer
@doc "Compute factorial"
def fac 1 do; 1 ;end # stop condition
def fac n do
n * fac n - 1
end
end
35. $ echo 'IO.puts "Hello World" | mix format -
IO.puts("Hello World”)
$ mix format --check-formatted
** (Mix) mix format failed due to --check-formatted.
The following files were not formatted:
* lib/fac.ex
Useful tools wit the
formatter