Diese Präsentation wurde erfolgreich gemeldet.
Die SlideShare-Präsentation wird heruntergeladen. ×

Elixir and Dialyzer, Types and Typespecs, using and understanding them

Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Wird geladen in …3
×

Hier ansehen

1 von 26 Anzeige

Elixir and Dialyzer, Types and Typespecs, using and understanding them

Herunterladen, um offline zu lesen

A Talk given at NYCElixir Feb 21, 2017 by Dan Janowski. An introduction to types in Elixir, success typing, and examples of how to write typespecs as well as including dialyzer in your app and some details on how to interpret the output.

A Talk given at NYCElixir Feb 21, 2017 by Dan Janowski. An introduction to types in Elixir, success typing, and examples of how to write typespecs as well as including dialyzer in your app and some details on how to interpret the output.

Anzeige
Anzeige

Weitere Verwandte Inhalte

Diashows für Sie (20)

Andere mochten auch (20)

Anzeige

Ähnlich wie Elixir and Dialyzer, Types and Typespecs, using and understanding them (20)

Anzeige

Elixir and Dialyzer, Types and Typespecs, using and understanding them

  1. 1. ELIXIR’S TYPE OF OPTIMISM HOW YOU CAN BE A TYPE OPTIMIST WITH DIALYZER AND TYPESPEC
  2. 2. INTRODUCTION TYPE BASICS ▸ Fundamental types: integer, atom, list, tuple, function ▸ Composite types: list of integers, tuple with varied members ▸ System of types: use of fundamental and composite types in functions, variables and structs.
  3. 3. INTRODUCTION ONE WAY TO THINK ABOUT A TYPE SYSTEM ▸ The function call structure is the wiring ▸ The types define the expected signal shapes going in and out. A type is a pattern, it defines structure and can define legal value ranges or enumerations.
  4. 4. INTRODUCTION TYPE CHECKING ▸ Static type checking ▸ Enforced during compilation ▸ Everything has to be right, declared ▸ Dynamic type checking ▸ Defined by use ▸ Runtime evaluation of operations at the fundamental level ▸ Pattern matching
  5. 5. OH BOY HOLY WAR OF TYPE TYPES WHICH IS BETTER?
  6. 6. ELIXIR TYPESPEC THE ELIXIR POSITION ▸ Dynamic typing ▸ Runtime enforcement of operations (“5” + 7) ▸ Pattern matching, guards on functions create some limits ▸ Testing can validate the intended use of a function
  7. 7. WHAT ABOUT TESTING ▸ Can test expected and unexpected inputs ▸ Exercise the code to validate type handling ▸ Developers are optimistic testers ▸ Easy to have unexpected input conditions ▸ Doesn’t protect future code. ▸ Best at validating a function’s transformation ELIXIR TYPESPEC
  8. 8. ELIXIR TYPESPEC THE SUPER POWER OF DIALYZER ▸ How is a function called by actual code? ▸ How does a function call other functions? ▸ Write signatures that are declarative ▸ Infer types up and down the call stack ▸ More exhaustive than what can be written as tests ▸ Your functions and structs are documented and validated
  9. 9. ELIXIR TYPESPEC DOWNSIDE ▸ Not DRY ▸ Define a struct ▸ Define a @type ▸ Write a function ▸ Write a @spec ▸ dialyzer takes a little time to run, output can be cryptic
  10. 10. DIALYZER DIALYZER READS ALL THE TYPES DECLARED ▸ Reads all the type specifications in the core modules, dependencies ▸ This takes a while, but is cached, done once. ▸ aka PLT, persistent lookup table ▸ Reads all the types you’ve declared
  11. 11. LET’S DO IT
  12. 12. SET UP ADD DIALYXIR TO MIX.EXS DEPS() defmodule Talk.Mixfile do use Mix.Project . . . defp deps do [ {:dialyxir, "~> 0.4", only: [:dev], runtime: false}, ] end end $ mix deps.get $ mix deps.compile Then
  13. 13. SET UP NEW MIX TASK $ mix helpmix # Runs the default task (current: "mix run") mix app.start # Starts all registered apps mix app.tree # Prints the application tree mix archive # Lists installed archives mix archive.build # Archives this project into a .ez file mix archive.install # Installs an archive locally mix archive.uninstall # Uninstalls archives mix clean # Deletes generated application files mix cmd # Executes the given command mix compile # Compiles source files mix deps # Lists dependencies and their status mix deps.clean # Deletes the given dependencies' files mix deps.compile # Compiles dependencies mix deps.get # Gets all out of date dependencies mix deps.tree # Prints the dependency tree mix deps.unlock # Unlocks the given dependencies mix deps.update # Updates the given dependencies mix dialyzer # Runs dialyzer with default or project-defined flags. mix do # Executes the tasks separated by comma mix escript # Lists installed escripts mix escript.build # Builds an escript for the project mix escript.install # Installs an escript locally mix escript.uninstall # Uninstalls escripts mix help # Prints help information for tasks mix hex # Prints Hex help information mix hex.build # Builds a new package version locally mix hex.config # Reads, updates or deletes Hex config mix hex.docs # Fetch or open documentation of a package mix hex.info # Prints Hex information mix hex.key # Manages Hex API key mix hex.outdated # Shows outdated Hex deps for the current project mix hex.owner # Manages Hex package ownership mix hex.public_keys # Manages Hex public keys mix hex.publish # Publishes a new package version mix hex.retire # Retires a package version mix hex.search # Searches for package names mix hex.user # Registers or manages Hex user mix loadconfig # Loads and persists the given configuration mix local # Lists local tasks mix local.hex # Installs Hex locally mix local.phoenix # Updates Phoenix locally mix local.public_keys # Manages public keys mix local.rebar # Installs Rebar locally mix new # Creates a new Elixir project mix phoenix.new # Creates a new Phoenix v1.2.1 application mix profile.fprof # Profiles the given file or expression with fprof mix run # Runs the given file or expression mix test # Runs a project's tests mix xref # Performs cross reference checks iex -S mix # Starts IEx and runs the default task
  14. 14. SET UP FIRST RUN flash:talk 03:32 525$ mix dialyzerChecking PLT... [:compiler, :elixir, :kernel, :logger, :stdlib] Finding suitable PLTs Looking up modules in dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt Looking up modules in dialyxir_erlang-19.2_elixir-1.4.1.plt Looking up modules in dialyxir_erlang-19.2.plt Finding applications for dialyxir_erlang-19.2.plt Finding modules for dialyxir_erlang-19.2.plt Creating dialyxir_erlang-19.2.plt Looking up modules in dialyxir_erlang-19.2.plt Removing 3 modules from dialyxir_erlang-19.2.plt Checking 11 modules in dialyxir_erlang-19.2.plt Adding 149 modules to dialyxir_erlang-19.2.plt Finding applications for dialyxir_erlang-19.2_elixir-1.4.1.plt Finding modules for dialyxir_erlang-19.2_elixir-1.4.1.plt Copying dialyxir_erlang-19.2.plt to dialyxir_erlang-19.2_elixir-1.4.1.plt Looking up modules in dialyxir_erlang-19.2_elixir-1.4.1.plt Checking 160 modules in dialyxir_erlang-19.2_elixir-1.4.1.plt Adding 220 modules to dialyxir_erlang-19.2_elixir-1.4.1.plt Finding applications for dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt Finding modules for dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt Copying dialyxir_erlang-19.2_elixir-1.4.1.plt to dialyxir_erlang-19.2_elixir-1. 4.1_deps-dev.plt Looking up modules in dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt Checking 380 modules in dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt Adding 57 modules to dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt Starting Dialyzer dialyzer --no_check_plt --fullpath --plt /Users/danj/Documents/elixir/typespec- talk/talk/_build/dev/dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt /Users/danj /Documents/elixir/typespec-talk/talk/_build/dev/lib/talk/ebin Proceeding with analysis... done in 0m1.38s done (passed successfully) flash:talk 03:38 526$ ‣ mix dialyzer ‣ First run on an empty project ~6min
  15. 15. CARDS CARD EXAMPLE defmodule Card do def kind({_suit,value}) when is_number(value), do: :number def kind({_suit,_value}), do: :face def check_cards(card) do IO.puts("kind(#{inspect(card)}) -> #{inspect(kind(card))}") end def main do check_cards({:spades, :king}) check_cards({:rubies, 10}) end end
  16. 16. CARDS DIALYZER NORMAL RESULT $ mix dialyzer Checking PLT... [:compiler, :elixir, :kernel, :logger, :stdlib] PLT is up to date! Starting Dialyzer dialyzer --no_check_plt --fullpath --plt /Users/danj/Documents/elixir/typespec- talk/talk/_build/dev/dialyxir_erlang-19.2_elixir-1.4.1_deps-dev.plt /Users/ danj/Documents/elixir/typespec-talk/talk/_build/dev/lib/talk/ebin Proceeding with analysis... done in 0m1.61s done (passed successfully)
  17. 17. CARDS ADD TYPES AND A SPEC 1defmodule Card do 2 7 11 12 def check_cards(card) do 13 IO.puts("kind(#{inspect(card)}) -> #{inspect(kind(card))}") 14 end 15 16 def main do 17 check_cards({:spades, :king}) 18 check_cards({:rubies, 10}) 19 end 20end 3 @type suit :: ( :spade | :heart | :club | :diamond ) 4 @type value :: ( 2..10 | :jack | :queen | :king | :ace ) 5 @type card :: { suit, value } 6 @type card_kind :: { :number | :face } 8 @spec kind(card()) :: card_kind() 9 def kind({_suit,value}) when is_number(value), do: :number 10 def kind({_suit,_value}), do: :face
  18. 18. CARDS Proceeding with analysis... lib/card.ex:8: Invalid type specification for function 'Elixir.Card':kind/1. The success typing is ({_,_}) -> 'face' | 'number' done in 0m1.72s done (warnings were emitted) 8 @spec kind(card()) :: card_kind() 5 @type card :: { suit, value } 6 @type card_kind :: { :number | :face } 6 @type card_kind :: ( :number | :face ) oops
  19. 19. CARDS Proceeding with analysis... lib/card.ex:16: Function main/0 has no local return lib/card.ex:17: The call 'Elixir.Card':check_cards({'spades','king'}) will never return since it differs in the 1st argument from the success typing arguments: ({'club','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'diamond','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'heart','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'spade','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}) lib/card.ex:18: The call 'Elixir.Card':check_cards({'rubies',10}) will never return since it differs in the 1st argument from the success typing arguments: ({'club','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'diamond','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'heart','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'spade','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10}) done in 0m1.75s done (warnings were emitted)
  20. 20. CARDS 17 check_cards({:spades, :king}) lib/card.ex:17: The call 'Elixir.Card':check_cards({'spades','king'}) will never return since it differs in the 1st argument from the success typing arguments: ({'club','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10} | {'diamond','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | | {'heart','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {'spade','ace' | 'jack' | 'king' | 'queen' | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 )
  21. 21. CARDS 1defmodule Card do 2 3 @type suit :: ( :spade | :heart | :club | :diamond ) 4 @type value :: ( 2..10 | :jack | :queen | :king | :ace ) 5 @type card :: { suit, value } 6 @type card_kind :: ( :number | :face ) 7 8 @spec kind(card()) :: card_kind() 9 def kind({_suit,value}) when is_number(value), do: :number 10 def kind({_suit,_value}), do: :face 11 12 @spec check_cards(card()) :: any() 13 def check_cards(card) do 14 IO.puts("kind(#{inspect(card)}) -> #{inspect(kind(card))}") 15 end 16 17 def main do 18 check_cards({:spade, :king}) 19 check_cards({:diamond, 10}) 20 end 21end
  22. 22. CARDS CARD EXAMPLE defmodule Card do def kind({_suit,value}) when is_number(value), do: :number def kind({_suit,_value}), do: :face def check_cards(card) do IO.puts("kind(#{inspect(card)}) -> #{inspect(kind(card))}") end def main do check_cards({:spades, :king}) check_cards({:rubies, 10}) end end
  23. 23. STRUCT TYPES AND STRUCT 1defmodule Card do 2 defstruct [:suit, :value] 3 4 @type suit :: ( :spade | :heart | :club | :diamond ) 5 @type value :: ( 2..10 | :jack | :queen | :king | :ace ) 6 7 @type t :: %Card{ suit: suit(), value: value()} 8 9 @type card_kind :: ( :number | :face ) 10 11 @spec kind(Card.t) :: card_kind() 12 def kind(%Card{value: value} = card) when is_number(value), do: :number 13 def kind(_card), do: :face 14end
  24. 24. ADDITIONAL RESOURCES
  25. 25. ADDITIONAL RESOURCES DOCUMENTATION ▸ http://learnyousomeerlang.com/dialyzer ▸ https://hexdocs.pm/elixir/typespecs.html ▸ https://hex.pm/packages/dialyxir ▸ http://user.it.uu.se/~kostis/Papers/succ_types.pdf ▸ Elixir Conf 2016 Jason Voegele Dialyzer talk
  26. 26. THANK YOU

×