SlideShare a Scribd company logo
1 of 36
Download to read offline
Elixir Formatter Internals
How that works under the hood to
make your code pretty.
Pedro Medeiros
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
Briefing
• Previous release of Elixir 1.5

• What is the Formatter

• Formatter internals

• Questions
State of Elixir 1.5
• Shipped features to help
developers and
documentation

• Breakpoints

• Exception Blame

• @impl
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
Iex breakpoint
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
@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
Elixir Goals
Productivity

Maintainability

Reliability
Elixir Goals
bit.ly/elixirgoals2017
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
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
Run the formatter
$ mix format _
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
What happened?
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
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
> raw = File.read!("lib/fac.ex")
> Code.Formatter.to_algebra(raw, opts)
Deeper inside
Code.format_string!
{:doc_group,
{:doc_cons, {:doc_string, "defmodule", 9},
{:doc_force,
{:doc_cons,
{:doc_cons,
{:doc_group,
{:doc_cons, {:doc_cons, " ", {:doc_nest, "Fac", :cursor, :break}}, " do"},
:self},
{:doc_nest,
{:doc_cons, :doc_nil,
{:doc_cons, :doc_line,
{:doc_group,
{:doc_force,
{:doc_cons,
{:doc_cons,
{:doc_cons,
{:doc_cons,
{:doc_group,
{:doc_cons, {:doc_string, "@spec", 5},
{:doc_group,
{:doc_cons,
{:doc_cons, " ",
{:doc_nest, {:doc_cons, {...}, ...}, :cursor, :break}},
:doc_nil}, :self}}, :self},
{:doc_cons, :doc_line,
{:doc_group,
{:doc_cons, {:doc_string, "@doc", 4},
{:doc_group, {:doc_cons, {:doc_cons, " ", {...}}, :doc_nil},
:self}}, :self}}},
{:doc_cons, :doc_line, {:doc_group, "# stop condition", :self}}},
{:doc_cons, :doc_line,
{:doc_group,
{:doc_cons,
{:doc_cons, {:doc_string, "def", 3},
{:doc_force,
{:doc_cons,
{:doc_cons, {:doc_group, {:doc_cons, ...}, :self},
{:doc_nest, {...}, ...}}, {:doc_cons, :doc_line, "end"}}}},
{:doc_cons, {:doc_collapse, 2}, {:doc_break, "", :strict}}},
:self}}},
{:doc_cons, :doc_line,
{:doc_group,
{:doc_cons, {:doc_break, "", :strict},
{:doc_cons, {:doc_string, "def", 3},
{:doc_force,
{:doc_cons,...
Deeper inside
Code.format_string!
Code.Formatter.to_algebra(raw)
:elixir.string_to_tokens/4
:elixir.tokens_to_quoted/3
Deeper inside
Code.format_string!
:elixir.string_to_tokens/4
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
Deeper inside
How an algebra is build for the formatter
:elixir.string_to_tokens
:elixir.tokens_to_quoted
[
{:identifier, {1, 1, nil}, :defmodule},
{:alias, {1, 11, nil}, :Fac},
{:do, {1, 15, nil}},
{:eol, {1, 17, 1}},
{:at_op, {2, 3, nil}, :@},
{:identifier, {2, 4, nil}, :spec},
{:paren_identifier, {2, 9, nil}, :fac},
{:"(", {2, 12, nil}},
{:identifier, {2, 13, nil}, :pos_integer},
{:")", {2, 24, nil}},
{:type_op, {2, 26, nil}, :::},
{:identifier, {2, 29, nil}, :pos_integer},
{:eol, {2, 40, 1}},
{:at_op, {3, 3, nil}, :@},
{:identifier, {3, 4, nil}, :doc},
{:bin_string, {3, 8, nil}, ["Compute factorial"]},
{:eol, {3, 27, 1}},
{:identifier, {4, 3, nil}, :def},
{:identifier, {4, 7, nil}, :fac},
{:int, {4, 11, 1}, '1'},
{:do, {4, 13, nil}},
{:";", {4, 15, 0}},
{:int, {4, 17, 1}, '1'},
{:";", {4, 19, 0}},
{:end, {4, 20, nil}},
{:eol, {4, 24, 1}},
{:identifier, {5, 3, nil}, :def},
{:identifier, {5, 7, nil}, :fac},
{:do_identifier, {5, 11, nil}, :n},
{:do, {5, 13, nil}},
{:eol, {5, 15, 1}},
{:identifier, {6, 5, nil}, :n},
{:mult_op, {6, 7, nil}, :*},
{:identifier, {6, 9, nil}, :fac},
{:identifier, {6, 13, nil}, :n},
{:dual_op, {6, 15, nil}, :-},
{:int, {6, 17, 1}, '1'},
{:eol, {6, 18, 1}},
{:end, {7, 3, nil}},
{:eol, {7, 6, 1}},
{:end, {8, 1, nil}},
{:eol, {8, 4, 1}}
]
Deeper inside
Code.format_string!
:elixir.tokens_to_quoted/3
[
{:identifier, {1, 1, nil}, :defmodule},
{:alias, {1, 11, nil}, :Fac},
{:do, {1, 15, nil}},
{:eol, {1, 17, 1}},
{:at_op, {2, 3, nil}, :@},
{:identifier, {2, 4, nil}, :spec},
{:paren_identifier, {2, 9, nil}, :fac},
{:"(", {2, 12, nil}},
{:identifier, {2, 13, nil}, :pos_integer},
{:")", {2, 24, nil}},
{:type_op, {2, 26, nil}, :::},
{:identifier, {2, 29, nil}, :pos_integer},
{:eol, {2, 40, 1}},
{:at_op, {3, 3, nil}, :@},
{:identifier, {3, 4, nil}, :doc}, ...
Deeper inside
Code.format_string!
{:defmodule, [no_parens: true, line: 1],
[
{:__aliases__, [line: 1], [:Fac]},
[
{{:__block__, [format: :block, end_line: 8, line: 1], [:do]},
{:__block__, [],
[
{:@, [line: 2],
[
{:spec, [no_parens: true, line: 2],
[
{:::, [line: 2],
[
{:fac, [end_line: 2, line: 2],
[{:pos_integer, [no_parens: true, line: 2], nil}]},
{:pos_integer, [no_parens: true, line: 2], nil}
]}
]}
]},
{:@, [newlines: 1, line: 3],
[
{:doc, [no_parens: true, line: 3],
[{:__block__, [format: :string, line: 3], ["Compute factorial"]}]}
]},
{:def, [newlines: 1, no_parens: true, line: 4],
[
{:fac, [no_parens: true, line: 4],
[{:__block__, [original: '1', line: 4], [1]}]},
[
{{:__block__, [format: :block, end_line: 4, line: 4], [:do]},
{:__block__, [original: '1', line: 4], [1]}}
]
]},
…
Deeper inside
Code.format_string!
quote do
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
end
{:defmodule, [context: Elixir, import: Kernel],
[
{:__aliases__, [alias: false], [:Fac]},
[
do: {:__block__, [],
[
{:@, [context: Elixir, import: Kernel],
[
{:spec, [context: Elixir],
[
{:::, [],
[
{:fac, [], [{:pos_integer, [], Elixir}]},
{:pos_integer, [], Elixir}
]}
]}
]},
{:@, [context: Elixir, import: Kernel],
[{:doc, [context: Elixir], ["Compute factorial"]}]},
{:def, [context: Elixir, import: Kernel],
[{:fac, [context: Elixir], [1]}, [do: 1]]},
{:def, [context: Elixir, import: Kernel],
[
{:fac, [context: Elixir], [{:n, [], Elixir}]},
[
do: {:*, [context: Elixir, import: Kernel],
[
{:n, [], Elixir},
{:fac, [],
[
{:-, [context: Elixir, import: Kernel],
[{:n, [], Elixir}, 1]}
]}
]}
]
]}
]}
]
]}
{:defmodule, [no_parens: true, line: 1],
[
{:__aliases__, [line: 1], [:Fac]},
[
{{:__block__, [format: :block, end_line: 8, line: 1], [:do]},
{:__block__, [],
[
{:@, [line: 2],
[
{:spec, [no_parens: true, line: 2],
[
{:::, [line: 2],
[
{:fac, [end_line: 2, line: 2],
[{:pos_integer, [no_parens: true, line: 2], nil}]},
{:pos_integer, [no_parens: true, line: 2], nil}
]}
]}
]},
{:@, [newlines: 1, line: 3],
[
{:doc, [no_parens: true, line: 3],
[{:__block__, [format: :string, line: 3], ["Compute factorial"]}]}
]},
{:def, [newlines: 1, no_parens: true, line: 4],
[
{:fac, [no_parens: true, line: 4],
[{:__block__, [original: '1', line: 4], [1]}]},
[
{{:__block__, [format: :block, end_line: 4, line: 4], [:do]},
{:__block__, [original: '1', line: 4], [1]}}
]
]},
{:def, [newlines: 1, no_parens: true, line: 5],
[
{:fac, [no_parens: true, line: 5],
[{:n, [no_parens: true, line: 5], nil}]},
[
{{:__block__, [format: :block, end_line: 7, line: 5], [:do]},
{:*, [line: 6], ...
Normal Quoted Code Formatter Quoted
Deeper inside
Code.format_string!
Code.Formatter.to_algebra(raw)
:elixir.string_to_tokens/4
:elixir.tokens_to_quoted/3
Code.Formatter.block_to_algebra/2
Deeper inside
Code.format_string!
{:doc_group,
{:doc_cons, {:doc_string, "defmodule", 9},
{:doc_force,
{:doc_cons,
{:doc_cons,
{:doc_group,
{:doc_cons, {:doc_cons, " ", {:doc_nest, "Fac", :cursor, :break}}, " do"},
:self},
{:doc_nest,
{:doc_cons, :doc_nil,
{:doc_cons, :doc_line,
{:doc_group,
{:doc_force,
{:doc_cons,
{:doc_cons,
{:doc_cons,
{:doc_cons,
{:doc_group,
{:doc_cons, {:doc_string, "@spec", 5},
{:doc_group,
{:doc_cons,
{:doc_cons, " ",
{:doc_nest, {:doc_cons, {...}, ...}, :cursor, :break}},
:doc_nil}, :self}}, :self},
{:doc_cons, :doc_line,
{:doc_group,
{:doc_cons, {:doc_string, "@doc", 4},
{:doc_group, {:doc_cons, {:doc_cons, " ", {...}}, :doc_nil},
:self}}, :self}}},
{:doc_cons, :doc_line, {:doc_group, "# stop condition", :self}}},
{:doc_cons, :doc_line,
{:doc_group,
{:doc_cons,
{:doc_cons, {:doc_string, "def", 3},
{:doc_force,
{:doc_cons,
{:doc_cons, {:doc_group, {:doc_cons, ...}, :self},
{:doc_nest, {...}, ...}}, {:doc_cons, :doc_line, "end"}}}},
{:doc_cons, {:doc_collapse, 2}, {:doc_break, "", :strict}}},
:self}}},
{:doc_cons, :doc_line,
{:doc_group,
{:doc_cons, {:doc_break, "", :strict},
{:doc_cons, {:doc_string, "def", 3},
{:doc_force,
{:doc_cons,...
Inspect.Algebra.format/2
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
Deeper inside
Code.format_string!
Code.Formatter.to_algebra(raw)
:elixir.string_to_tokens/4
:elixir.tokens_to_quoted/3
Code.Formatter.block_to_algebra/2
Inspect.Algebra.format/2
# insert comments to the quoted code here
Deeper inside
Code.format_string!
%% elixir.erl
'string_to_quoted!'(String, StartLine, File, Opts) ->
case string_to_tokens(String, StartLine, File, Opts) of
{ok, Tokens} ->
case tokens_to_quoted(Tokens, File, Opts) of
{ok, Forms} ->
Forms;
{error, {Line, Error, Token}} ->
elixir_errors:parse_error(Line, File, Error, Token)
end;
{error, {Line, Error, Token}} ->
elixir_errors:parse_error(Line, File, Error, Token)
end.
$ 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
Thanks

More Related Content

What's hot

Java script introducation & basics
Java script introducation & basicsJava script introducation & basics
Java script introducation & basics
H K
 

What's hot (20)

Let's build a parser!
Let's build a parser!Let's build a parser!
Let's build a parser!
 
Basics of Python programming (part 2)
Basics of Python programming (part 2)Basics of Python programming (part 2)
Basics of Python programming (part 2)
 
901131 examples
901131 examples901131 examples
901131 examples
 
Understanding static analysis php amsterdam 2018
Understanding static analysis   php amsterdam 2018Understanding static analysis   php amsterdam 2018
Understanding static analysis php amsterdam 2018
 
Slaying the Dragon: Implementing a Programming Language in Ruby
Slaying the Dragon: Implementing a Programming Language in RubySlaying the Dragon: Implementing a Programming Language in Ruby
Slaying the Dragon: Implementing a Programming Language in Ruby
 
Graph Database Query Languages
Graph Database Query LanguagesGraph Database Query Languages
Graph Database Query Languages
 
Clean code
Clean codeClean code
Clean code
 
PHP Scripting
PHP ScriptingPHP Scripting
PHP Scripting
 
Hiphop php
Hiphop phpHiphop php
Hiphop php
 
Java script introducation & basics
Java script introducation & basicsJava script introducation & basics
Java script introducation & basics
 
Notes for GNU Octave - Numerical Programming - for Students - 02 of 02 by aru...
Notes for GNU Octave - Numerical Programming - for Students - 02 of 02 by aru...Notes for GNU Octave - Numerical Programming - for Students - 02 of 02 by aru...
Notes for GNU Octave - Numerical Programming - for Students - 02 of 02 by aru...
 
Closure, Higher-order function in Swift
Closure, Higher-order function in SwiftClosure, Higher-order function in Swift
Closure, Higher-order function in Swift
 
Python Fundamentals - Basic
Python Fundamentals - BasicPython Fundamentals - Basic
Python Fundamentals - Basic
 
Having Fun Programming!
Having Fun Programming!Having Fun Programming!
Having Fun Programming!
 
The Error of Our Ways
The Error of Our WaysThe Error of Our Ways
The Error of Our Ways
 
Python Workshop Part 2. LUG Maniapl
Python Workshop Part 2. LUG ManiaplPython Workshop Part 2. LUG Maniapl
Python Workshop Part 2. LUG Maniapl
 
Protocols
ProtocolsProtocols
Protocols
 
C Programming Project
C Programming ProjectC Programming Project
C Programming Project
 
Class 8 - Database Programming
Class 8 - Database ProgrammingClass 8 - Database Programming
Class 8 - Database Programming
 
Declarative Thinking, Declarative Practice
Declarative Thinking, Declarative PracticeDeclarative Thinking, Declarative Practice
Declarative Thinking, Declarative Practice
 

Similar to Elixir formatter Internals

ECMAScript2015
ECMAScript2015ECMAScript2015
ECMAScript2015
qmmr
 
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code styleRuby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Anton Shemerey
 

Similar to Elixir formatter Internals (20)

Round PEG, Round Hole - Parsing Functionally
Round PEG, Round Hole - Parsing FunctionallyRound PEG, Round Hole - Parsing Functionally
Round PEG, Round Hole - Parsing Functionally
 
Introducing Elixir and OTP at the Erlang BASH
Introducing Elixir and OTP at the Erlang BASHIntroducing Elixir and OTP at the Erlang BASH
Introducing Elixir and OTP at the Erlang BASH
 
Elixir & Phoenix - fast, concurrent and explicit
Elixir & Phoenix - fast, concurrent and explicitElixir & Phoenix - fast, concurrent and explicit
Elixir & Phoenix - fast, concurrent and explicit
 
Creating Domain Specific Languages in Python
Creating Domain Specific Languages in PythonCreating Domain Specific Languages in Python
Creating Domain Specific Languages in Python
 
A Few of My Favorite (Python) Things
A Few of My Favorite (Python) ThingsA Few of My Favorite (Python) Things
A Few of My Favorite (Python) Things
 
Blocks by Lachs Cox
Blocks by Lachs CoxBlocks by Lachs Cox
Blocks by Lachs Cox
 
GE8151 Problem Solving and Python Programming
GE8151 Problem Solving and Python ProgrammingGE8151 Problem Solving and Python Programming
GE8151 Problem Solving and Python Programming
 
PHP PPT FILE
PHP PPT FILEPHP PPT FILE
PHP PPT FILE
 
Elixir & Phoenix – fast, concurrent and explicit
Elixir & Phoenix – fast, concurrent and explicitElixir & Phoenix – fast, concurrent and explicit
Elixir & Phoenix – fast, concurrent and explicit
 
Elixir cheatsheet
Elixir cheatsheetElixir cheatsheet
Elixir cheatsheet
 
ECMAScript2015
ECMAScript2015ECMAScript2015
ECMAScript2015
 
Introducing Elixir
Introducing ElixirIntroducing Elixir
Introducing Elixir
 
CoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love AffairCoffeeScript - A Rubyist's Love Affair
CoffeeScript - A Rubyist's Love Affair
 
Steady with ruby
Steady with rubySteady with ruby
Steady with ruby
 
Es6 hackathon
Es6 hackathonEs6 hackathon
Es6 hackathon
 
Is Haskell an acceptable Perl?
Is Haskell an acceptable Perl?Is Haskell an acceptable Perl?
Is Haskell an acceptable Perl?
 
Python slide
Python slidePython slide
Python slide
 
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code styleRuby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
Ruby: OOP, metaprogramming, blocks, iterators, mix-ins, duck typing. Code style
 
Ruby - Uma Introdução
Ruby - Uma IntroduçãoRuby - Uma Introdução
Ruby - Uma Introdução
 
python codes
python codespython codes
python codes
 

Recently uploaded

Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝
soniya singh
 
Call Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝
soniya singh
 
AWS Community DAY Albertini-Ellan Cloud Security (1).pptx
AWS Community DAY Albertini-Ellan Cloud Security (1).pptxAWS Community DAY Albertini-Ellan Cloud Security (1).pptx
AWS Community DAY Albertini-Ellan Cloud Security (1).pptx
ellan12
 
Hot Service (+9316020077 ) Goa Call Girls Real Photos and Genuine Service
Hot Service (+9316020077 ) Goa  Call Girls Real Photos and Genuine ServiceHot Service (+9316020077 ) Goa  Call Girls Real Photos and Genuine Service
Hot Service (+9316020077 ) Goa Call Girls Real Photos and Genuine Service
sexy call girls service in goa
 
Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...
Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...
Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...
Sheetaleventcompany
 
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Call Girls In Delhi Whatsup 9873940964 Enjoy Unlimited Pleasure
 

Recently uploaded (20)

Pune Airport ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready...
Pune Airport ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready...Pune Airport ( Call Girls ) Pune  6297143586  Hot Model With Sexy Bhabi Ready...
Pune Airport ( Call Girls ) Pune 6297143586 Hot Model With Sexy Bhabi Ready...
 
Russian Call girl in Ajman +971563133746 Ajman Call girl Service
Russian Call girl in Ajman +971563133746 Ajman Call girl ServiceRussian Call girl in Ajman +971563133746 Ajman Call girl Service
Russian Call girl in Ajman +971563133746 Ajman Call girl Service
 
Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Sukhdev Vihar Delhi 💯Call Us 🔝8264348440🔝
 
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort ServiceEnjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
Enjoy Night⚡Call Girls Dlf City Phase 3 Gurgaon >༒8448380779 Escort Service
 
Call Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Prashant Vihar, Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
 
Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝
Call Girls In Defence Colony Delhi 💯Call Us 🔝8264348440🔝
 
(+971568250507 ))# Young Call Girls in Ajman By Pakistani Call Girls in ...
(+971568250507  ))#  Young Call Girls  in Ajman  By Pakistani Call Girls  in ...(+971568250507  ))#  Young Call Girls  in Ajman  By Pakistani Call Girls  in ...
(+971568250507 ))# Young Call Girls in Ajman By Pakistani Call Girls in ...
 
AWS Community DAY Albertini-Ellan Cloud Security (1).pptx
AWS Community DAY Albertini-Ellan Cloud Security (1).pptxAWS Community DAY Albertini-Ellan Cloud Security (1).pptx
AWS Community DAY Albertini-Ellan Cloud Security (1).pptx
 
Hot Service (+9316020077 ) Goa Call Girls Real Photos and Genuine Service
Hot Service (+9316020077 ) Goa  Call Girls Real Photos and Genuine ServiceHot Service (+9316020077 ) Goa  Call Girls Real Photos and Genuine Service
Hot Service (+9316020077 ) Goa Call Girls Real Photos and Genuine Service
 
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
𓀤Call On 7877925207 𓀤 Ahmedguda Call Girls Hot Model With Sexy Bhabi Ready Fo...
 
Top Rated Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...
Top Rated  Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...Top Rated  Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...
Top Rated Pune Call Girls Daund ⟟ 6297143586 ⟟ Call Me For Genuine Sex Servi...
 
Al Barsha Night Partner +0567686026 Call Girls Dubai
Al Barsha Night Partner +0567686026 Call Girls  DubaiAl Barsha Night Partner +0567686026 Call Girls  Dubai
Al Barsha Night Partner +0567686026 Call Girls Dubai
 
Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...
Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...
Call Girls Service Chandigarh Lucky ❤️ 7710465962 Independent Call Girls In C...
 
Russian Call Girls in %(+971524965298 )# Call Girls in Dubai
Russian Call Girls in %(+971524965298  )#  Call Girls in DubaiRussian Call Girls in %(+971524965298  )#  Call Girls in Dubai
Russian Call Girls in %(+971524965298 )# Call Girls in Dubai
 
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
Hire↠Young Call Girls in Tilak nagar (Delhi) ☎️ 9205541914 ☎️ Independent Esc...
 
Call Now ☎ 8264348440 !! Call Girls in Shahpur Jat Escort Service Delhi N.C.R.
Call Now ☎ 8264348440 !! Call Girls in Shahpur Jat Escort Service Delhi N.C.R.Call Now ☎ 8264348440 !! Call Girls in Shahpur Jat Escort Service Delhi N.C.R.
Call Now ☎ 8264348440 !! Call Girls in Shahpur Jat Escort Service Delhi N.C.R.
 
VVVIP Call Girls In Connaught Place ➡️ Delhi ➡️ 9999965857 🚀 No Advance 24HRS...
VVVIP Call Girls In Connaught Place ➡️ Delhi ➡️ 9999965857 🚀 No Advance 24HRS...VVVIP Call Girls In Connaught Place ➡️ Delhi ➡️ 9999965857 🚀 No Advance 24HRS...
VVVIP Call Girls In Connaught Place ➡️ Delhi ➡️ 9999965857 🚀 No Advance 24HRS...
 
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
Dwarka Sector 26 Call Girls | Delhi | 9999965857 🫦 Vanshika Verma More Our Se...
 
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
(INDIRA) Call Girl Pune Call Now 8250077686 Pune Escorts 24x7
 
VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...
VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...
VIP Model Call Girls NIBM ( Pune ) Call ON 8005736733 Starting From 5K to 25K...
 

Elixir formatter Internals

  • 1. Elixir Formatter Internals How that works under the hood to make your code pretty. Pedro Medeiros
  • 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
  • 3. Briefing • Previous release of Elixir 1.5 • What is the Formatter • Formatter internals • Questions
  • 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
  • 9.
  • 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
  • 14. Run the formatter $ mix format _
  • 15. 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
  • 19. > raw = File.read!("lib/fac.ex") > Code.Formatter.to_algebra(raw, opts) Deeper inside Code.format_string! {:doc_group, {:doc_cons, {:doc_string, "defmodule", 9}, {:doc_force, {:doc_cons, {:doc_cons, {:doc_group, {:doc_cons, {:doc_cons, " ", {:doc_nest, "Fac", :cursor, :break}}, " do"}, :self}, {:doc_nest, {:doc_cons, :doc_nil, {:doc_cons, :doc_line, {:doc_group, {:doc_force, {:doc_cons, {:doc_cons, {:doc_cons, {:doc_cons, {:doc_group, {:doc_cons, {:doc_string, "@spec", 5}, {:doc_group, {:doc_cons, {:doc_cons, " ", {:doc_nest, {:doc_cons, {...}, ...}, :cursor, :break}}, :doc_nil}, :self}}, :self}, {:doc_cons, :doc_line, {:doc_group, {:doc_cons, {:doc_string, "@doc", 4}, {:doc_group, {:doc_cons, {:doc_cons, " ", {...}}, :doc_nil}, :self}}, :self}}}, {:doc_cons, :doc_line, {:doc_group, "# stop condition", :self}}}, {:doc_cons, :doc_line, {:doc_group, {:doc_cons, {:doc_cons, {:doc_string, "def", 3}, {:doc_force, {:doc_cons, {:doc_cons, {:doc_group, {:doc_cons, ...}, :self}, {:doc_nest, {...}, ...}}, {:doc_cons, :doc_line, "end"}}}}, {:doc_cons, {:doc_collapse, 2}, {:doc_break, "", :strict}}}, :self}}}, {:doc_cons, :doc_line, {:doc_group, {:doc_cons, {:doc_break, "", :strict}, {:doc_cons, {:doc_string, "def", 3}, {:doc_force, {:doc_cons,...
  • 21. Deeper inside Code.format_string! :elixir.string_to_tokens/4 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
  • 22. Deeper inside How an algebra is build for the formatter :elixir.string_to_tokens :elixir.tokens_to_quoted [ {:identifier, {1, 1, nil}, :defmodule}, {:alias, {1, 11, nil}, :Fac}, {:do, {1, 15, nil}}, {:eol, {1, 17, 1}}, {:at_op, {2, 3, nil}, :@}, {:identifier, {2, 4, nil}, :spec}, {:paren_identifier, {2, 9, nil}, :fac}, {:"(", {2, 12, nil}}, {:identifier, {2, 13, nil}, :pos_integer}, {:")", {2, 24, nil}}, {:type_op, {2, 26, nil}, :::}, {:identifier, {2, 29, nil}, :pos_integer}, {:eol, {2, 40, 1}}, {:at_op, {3, 3, nil}, :@}, {:identifier, {3, 4, nil}, :doc}, {:bin_string, {3, 8, nil}, ["Compute factorial"]}, {:eol, {3, 27, 1}}, {:identifier, {4, 3, nil}, :def}, {:identifier, {4, 7, nil}, :fac}, {:int, {4, 11, 1}, '1'}, {:do, {4, 13, nil}}, {:";", {4, 15, 0}}, {:int, {4, 17, 1}, '1'}, {:";", {4, 19, 0}}, {:end, {4, 20, nil}}, {:eol, {4, 24, 1}}, {:identifier, {5, 3, nil}, :def}, {:identifier, {5, 7, nil}, :fac}, {:do_identifier, {5, 11, nil}, :n}, {:do, {5, 13, nil}}, {:eol, {5, 15, 1}}, {:identifier, {6, 5, nil}, :n}, {:mult_op, {6, 7, nil}, :*}, {:identifier, {6, 9, nil}, :fac}, {:identifier, {6, 13, nil}, :n}, {:dual_op, {6, 15, nil}, :-}, {:int, {6, 17, 1}, '1'}, {:eol, {6, 18, 1}}, {:end, {7, 3, nil}}, {:eol, {7, 6, 1}}, {:end, {8, 1, nil}}, {:eol, {8, 4, 1}} ]
  • 23. Deeper inside Code.format_string! :elixir.tokens_to_quoted/3 [ {:identifier, {1, 1, nil}, :defmodule}, {:alias, {1, 11, nil}, :Fac}, {:do, {1, 15, nil}}, {:eol, {1, 17, 1}}, {:at_op, {2, 3, nil}, :@}, {:identifier, {2, 4, nil}, :spec}, {:paren_identifier, {2, 9, nil}, :fac}, {:"(", {2, 12, nil}}, {:identifier, {2, 13, nil}, :pos_integer}, {:")", {2, 24, nil}}, {:type_op, {2, 26, nil}, :::}, {:identifier, {2, 29, nil}, :pos_integer}, {:eol, {2, 40, 1}}, {:at_op, {3, 3, nil}, :@}, {:identifier, {3, 4, nil}, :doc}, ...
  • 24. Deeper inside Code.format_string! {:defmodule, [no_parens: true, line: 1], [ {:__aliases__, [line: 1], [:Fac]}, [ {{:__block__, [format: :block, end_line: 8, line: 1], [:do]}, {:__block__, [], [ {:@, [line: 2], [ {:spec, [no_parens: true, line: 2], [ {:::, [line: 2], [ {:fac, [end_line: 2, line: 2], [{:pos_integer, [no_parens: true, line: 2], nil}]}, {:pos_integer, [no_parens: true, line: 2], nil} ]} ]} ]}, {:@, [newlines: 1, line: 3], [ {:doc, [no_parens: true, line: 3], [{:__block__, [format: :string, line: 3], ["Compute factorial"]}]} ]}, {:def, [newlines: 1, no_parens: true, line: 4], [ {:fac, [no_parens: true, line: 4], [{:__block__, [original: '1', line: 4], [1]}]}, [ {{:__block__, [format: :block, end_line: 4, line: 4], [:do]}, {:__block__, [original: '1', line: 4], [1]}} ] ]}, …
  • 25. Deeper inside Code.format_string! quote do 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 end
  • 26. {:defmodule, [context: Elixir, import: Kernel], [ {:__aliases__, [alias: false], [:Fac]}, [ do: {:__block__, [], [ {:@, [context: Elixir, import: Kernel], [ {:spec, [context: Elixir], [ {:::, [], [ {:fac, [], [{:pos_integer, [], Elixir}]}, {:pos_integer, [], Elixir} ]} ]} ]}, {:@, [context: Elixir, import: Kernel], [{:doc, [context: Elixir], ["Compute factorial"]}]}, {:def, [context: Elixir, import: Kernel], [{:fac, [context: Elixir], [1]}, [do: 1]]}, {:def, [context: Elixir, import: Kernel], [ {:fac, [context: Elixir], [{:n, [], Elixir}]}, [ do: {:*, [context: Elixir, import: Kernel], [ {:n, [], Elixir}, {:fac, [], [ {:-, [context: Elixir, import: Kernel], [{:n, [], Elixir}, 1]} ]} ]} ] ]} ]} ] ]} {:defmodule, [no_parens: true, line: 1], [ {:__aliases__, [line: 1], [:Fac]}, [ {{:__block__, [format: :block, end_line: 8, line: 1], [:do]}, {:__block__, [], [ {:@, [line: 2], [ {:spec, [no_parens: true, line: 2], [ {:::, [line: 2], [ {:fac, [end_line: 2, line: 2], [{:pos_integer, [no_parens: true, line: 2], nil}]}, {:pos_integer, [no_parens: true, line: 2], nil} ]} ]} ]}, {:@, [newlines: 1, line: 3], [ {:doc, [no_parens: true, line: 3], [{:__block__, [format: :string, line: 3], ["Compute factorial"]}]} ]}, {:def, [newlines: 1, no_parens: true, line: 4], [ {:fac, [no_parens: true, line: 4], [{:__block__, [original: '1', line: 4], [1]}]}, [ {{:__block__, [format: :block, end_line: 4, line: 4], [:do]}, {:__block__, [original: '1', line: 4], [1]}} ] ]}, {:def, [newlines: 1, no_parens: true, line: 5], [ {:fac, [no_parens: true, line: 5], [{:n, [no_parens: true, line: 5], nil}]}, [ {{:__block__, [format: :block, end_line: 7, line: 5], [:do]}, {:*, [line: 6], ... Normal Quoted Code Formatter Quoted
  • 28. Deeper inside Code.format_string! {:doc_group, {:doc_cons, {:doc_string, "defmodule", 9}, {:doc_force, {:doc_cons, {:doc_cons, {:doc_group, {:doc_cons, {:doc_cons, " ", {:doc_nest, "Fac", :cursor, :break}}, " do"}, :self}, {:doc_nest, {:doc_cons, :doc_nil, {:doc_cons, :doc_line, {:doc_group, {:doc_force, {:doc_cons, {:doc_cons, {:doc_cons, {:doc_cons, {:doc_group, {:doc_cons, {:doc_string, "@spec", 5}, {:doc_group, {:doc_cons, {:doc_cons, " ", {:doc_nest, {:doc_cons, {...}, ...}, :cursor, :break}}, :doc_nil}, :self}}, :self}, {:doc_cons, :doc_line, {:doc_group, {:doc_cons, {:doc_string, "@doc", 4}, {:doc_group, {:doc_cons, {:doc_cons, " ", {...}}, :doc_nil}, :self}}, :self}}}, {:doc_cons, :doc_line, {:doc_group, "# stop condition", :self}}}, {:doc_cons, :doc_line, {:doc_group, {:doc_cons, {:doc_cons, {:doc_string, "def", 3}, {:doc_force, {:doc_cons, {:doc_cons, {:doc_group, {:doc_cons, ...}, :self}, {:doc_nest, {...}, ...}}, {:doc_cons, :doc_line, "end"}}}}, {:doc_cons, {:doc_collapse, 2}, {:doc_break, "", :strict}}}, :self}}}, {:doc_cons, :doc_line, {:doc_group, {:doc_cons, {:doc_break, "", :strict}, {:doc_cons, {:doc_string, "def", 3}, {:doc_force, {:doc_cons,...
  • 30.
  • 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
  • 33. Deeper inside Code.format_string! %% elixir.erl 'string_to_quoted!'(String, StartLine, File, Opts) -> case string_to_tokens(String, StartLine, File, Opts) of {ok, Tokens} -> case tokens_to_quoted(Tokens, File, Opts) of {ok, Forms} -> Forms; {error, {Line, Error, Token}} -> elixir_errors:parse_error(Line, File, Error, Token) end; {error, {Line, Error, Token}} -> elixir_errors:parse_error(Line, File, Error, Token) end.
  • 34.
  • 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