Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Refactoring Elixir - Lessons Learned from a Year on exercism.io

Refactoring Elixir - Lessons Learned from a Year on exercism.io

There's a lot you can learn about the pitfalls of a language when you see literally hundreds of people all solving the same problems - and frequently making the same mistakes! In this talk we're going to start with some common, but less than optimal, solutions to some of the problems on exercism.io, and we'll do a step by step refactoring. We'll also add in some benchmarking to the existing exercism.io test suites to show the performance benefits of some of the refactoring that we do, and discuss the tradeoffs of the decisions that we're making to illustrate why this refactoring is beneficial.
Devon is a Software Engineer at EducationSuperHighway. When he's not writing Ruby at work, he can be found contributing to open source projects in Elixir. Away from the computer he's a husband, father and chess player.

Devon Estes

May 04, 2017
Tweet

More Decks by Devon Estes

Other Decks in Programming

Transcript

  1. REFACTORING IS A CONTROLLED TECHNIQUE FOR IMPROVING THE DESIGN OF

    AN EXISTING CODE BASE. ITS ESSENCE IS APPLYING A SERIES OF SMALL BEHAVIOR- PRESERVING TRANSFORMATIONS, EACH OF WHICH IS "TOO SMALL TO BE WORTH DOING”. Martin Fowler Devon Estes ElixirConf EU 2017 @devoncestes
  2. Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Easier

    to change •Lower complexity •Increase performance WHY REFACTOR?
  3. Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Easier

    to change •Lower complexity •Increase performance* *Some people think this isn’t really part of refactoring. I do. WHY REFACTOR?
  4. Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Easier

    to change •Lower complexity •Increase performance* *Some people think this isn’t really part of refactoring. I do. WHY REFACTOR?
  5. Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Lower

    complexity •Increase performance* *Some people think this isn’t really part of refactoring. I do. WHY REFACTOR?
  6. Devon Estes ElixirConf EU 2017 @devoncestes EXERCISM IS A PLACE

    WHERE PEOPLE CAN IMPROVE THEIR PROGRAMMING SKILLS BY SOLVING TOY PROBLEMS AND THEN HAVING FRIENDLY AND RICH DISCUSSIONS ABOUT THE WAYS TO MAKE THE CODE BETTER. Katrina Owen
  7. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do end end
  8. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) end end
  9. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) cond do end end end
  10. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) cond do bottom > top -> :not_found end end end
  11. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) cond do bottom > top -> :not_found middle_elem == value -> {:ok, middle_idx} end end end
  12. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) cond do bottom > top -> :not_found middle_elem == value -> {:ok, middle_idx} middle_elem < value -> search(items, value, middle_idx + 1, top) end end end
  13. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) cond do bottom > top -> :not_found middle_elem == value -> {:ok, middle_idx} middle_elem < value -> search(items, value, middle_idx + 1, top) true -> search(items, value, bottom, middle_idx - 1) end end end
  14. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def

    search(items, value) do search(items, value, 0, tuple_size(items) - 1) end defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) cond do bottom > top -> :not_found middle_elem == value -> {:ok, middle_idx} middle_elem < value -> search(items, value, middle_idx + 1, top) true -> search(items, value, bottom, middle_idx - 1) end end end
  15. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) end
  16. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) search(items, value, bottom, top, middle_idx, middle_elem) end end
  17. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) search(items, value, bottom, top, middle_idx, middle_elem) end defp search(_, value, _, _, middle_idx, middle_elem) when value == middle_elem, do: {:ok, middle_idx} end
  18. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) search(items, value, bottom, top, middle_idx, middle_elem) end defp search(_, value, _, _, middle_idx, middle_elem) when value == middle_elem, do: {:ok, middle_idx} defp search(_, _, bottom, top, _, _) when bottom > top, do: :not_found end
  19. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) search(items, value, bottom, top, middle_idx, middle_elem) end defp search(_, value, _, _, middle_idx, middle_elem) when value == middle_elem, do: {:ok, middle_idx} defp search(_, _, bottom, top, _, _) when bottom > top, do: :not_found defp search(items, value, _, top, middle_idx, middle_elem) when middle_elem < value, do: search(items, value, middle_idx + 1, top) end
  20. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) defp search(items, value, bottom, top) do middle_idx = div(bottom + top, 2) middle_elem = elem(items, middle_idx) search(items, value, bottom, top, middle_idx, middle_elem) end defp search(_, value, _, _, middle_idx, middle_elem) when value == middle_elem, do: {:ok, middle_idx} defp search(_, _, bottom, top, _, _) when bottom > top, do: :not_found defp search(items, value, _, top, middle_idx, middle_elem) when middle_elem < value, do: search(items, value, middle_idx + 1, top) defp search(items, value, bottom, _, middle_idx, middle_elem) do: search(items, value, bottom, middle_idx - 1) end
  21. Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def

    search(items, value), do: search(items, value, 0, tuple_size(items) - 1) defp search(items, value, bottom, top), do: search(items, value, bottom, top, div(bottom + top, 2)) defp search(items, value, bottom, top, middle_idx), do: search(items, value, bottom, top, middle_idx, elem(items, middle_idx)) defp search(_, value, _, _, middle_idx, middle_elem) when value == middle_elem, do: {:ok, middle_idx} defp search(_, _, bottom, top, _, _) when bottom > top, do: :not_found defp search(items, value, _, top, middle_idx, middle_elem) when middle_elem < value, do: search(items, value, middle_idx + 1, top) defp search(items, value, bottom, _, middle_idx, middle_elem) do: search(items, value, bottom, middle_idx - 1) end
  22. Devon Estes ElixirConf EU 2017 @devoncestes bench_func = fn(module) ->

    module.search({1, 2, 3, 4, 5}, 1) module.search({1, 2, 3, 4, 5}, 3) module.search({1, 2, 3, 4, 5}, 5) module.search({1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}, 3) module.search({1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}, 34) module.search({1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}, 377) end
  23. Devon Estes ElixirConf EU 2017 @devoncestes bench_func = fn(module) ->

    module.search({1, 2, 3, 4, 5}, 1) module.search({1, 2, 3, 4, 5}, 3) module.search({1, 2, 3, 4, 5}, 5) module.search({1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}, 3) module.search({1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}, 34) module.search({1, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377}, 377) end Benchee.run(%{ "pattern matching" => fn -> bench_func.(BinarySearch.PatternMatching) end, "cond statement" => fn -> bench_func.(BinarySearch.Cond) end, }, time: 10)
  24. Devon Estes ElixirConf EU 2017 @devoncestes Name ips average deviation

    median cond statement 763.25 K 1.31 μs ±379.72% 1.20 μs pattern matching 717.96 K 1.39 μs ±362.90% 1.20 μs Comparison: cond statement 763.25 K pattern matching 717.96 K - 1.06x slower
  25. Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching

    Cond Statement Readability Complexity Performance
  26. Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching

    Cond Statement Readability Complexity Performance
  27. Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching

    Cond Statement Readability Complexity Performance
  28. Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching

    Cond Statement Readability Complexity Performance
  29. Devon Estes ElixirConf EU 2017 @devoncestes defmodule SpaceAge.MapLookup do def

    age_on(planet, seconds) do lookup = %{ earth: 1.0, mercury: 0.2408467, venus: 0.61519726, mars: 1.8808158, jupiter: 11.862615, saturn: 29.447498, uranus: 84.016846, neptune: 164.79132 } end end
  30. Devon Estes ElixirConf EU 2017 @devoncestes defmodule SpaceAge.MapLookup do def

    age_on(planet, seconds) do lookup = %{ earth: 1.0, mercury: 0.2408467, venus: 0.61519726, mars: 1.8808158, jupiter: 11.862615, saturn: 29.447498, uranus: 84.016846, neptune: 164.79132 } seconds / (lookup[planet] * 31557600) end end
  31. Devon Estes ElixirConf EU 2017 @devoncestes defmodule SpaceAge.MapLookup do @earth_year

    31557600 def age_on(planet, seconds) do lookup = %{ earth: 1.0, mercury: 0.2408467, venus: 0.61519726, mars: 1.8808158, jupiter: 11.862615, saturn: 29.447498, uranus: 84.016846, neptune: 164.79132 } seconds / (lookup[planet] * @earth_year) end end
  32. Devon Estes ElixirConf EU 2017 @devoncestes defmodule SpaceAge.MapLookup do @earth_year

    31557600 @planet_year_lengths %{ earth: 1.0, mercury: 0.2408467, venus: 0.61519726, mars: 1.8808158, jupiter: 11.862615, saturn: 29.447498, uranus: 84.016846, neptune: 164.79132 } def age_on(planet, seconds), do: seconds / (@planet_year_lengths[planet] * @earth_year) end
  33. Devon Estes ElixirConf EU 2017 @devoncestes defmodule SpaceAge.PatternMatching do @earth_year

    31557600 def age_on(planet, seconds), do: seconds / (planet_rel_years(planet) * @earth_year) defp planet_rel_years(:mercury), do: 0.2408467 defp planet_rel_years(:venus), do: 0.61519726 defp planet_rel_years(:earth), do: 1.0 defp planet_rel_years(:mars), do: 1.8808158 defp planet_rel_years(:jupiter), do: 11.862615 defp planet_rel_years(:saturn), do: 29.447498 defp planet_rel_years(:uranus), do: 84.016846 defp planet_rel_years(:neptune), do: 164.79132 end
  34. Devon Estes ElixirConf EU 2017 @devoncestes Name ips average deviation

    median pattern matching 1.54 M 0.65 μs ±1081.97% 0.60 μs map lookup 0.82 M 1.22 μs ±440.35% 1.10 μs Comparison: pattern matching 1.54 M map lookup 0.82 M - 1.88x slower
  35. Devon Estes ElixirConf EU 2017 @devoncestes defmodule SpaceAge.Macros do @earth_year

    31557600 def age_on(planet, seconds), do: seconds / planet_rel_years(planet) defp planet_rel_years(:mercury), do: unquote(0.2408467 * @earth_year) defp planet_rel_years(:venus), do: unquote(0.61519726 * @earth_year) defp planet_rel_years(:earth), do: unquote(@earth_year) defp planet_rel_years(:mars), do: unquote(1.8808158 * @earth_year) defp planet_rel_years(:jupiter), do: unquote(11.862615 * @earth_year) defp planet_rel_years(:saturn), do: unquote(29.447498 * @earth_year) defp planet_rel_years(:uranus), do: unquote(84.016846 * @earth_year) defp planet_rel_years(:neptune), do: unquote(164.79132 * @earth_year) end
  36. Devon Estes ElixirConf EU 2017 @devoncestes Name ips average deviation

    median macros 1.85 M 0.54 μs ±1178.22% 0.50 μs pattern matching 1.58 M 0.63 μs ±1133.03% 0.60 μs map lookup 0.88 M 1.14 μs ±471.56% 1.00 μs Comparison: macros 1.85 M pattern matching 1.58 M - 1.17x slower map lookup 0.88 M - 2.10x slower
  37. Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Pattern Matching

    Map Lookup Macros Readability Lower Complexity Performance
  38. Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Pattern Matching

    Map Lookup Macros Readability Lower Complexity Performance
  39. Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Pattern Matching

    Map Lookup Macros Readability Lower Complexity Performance
  40. Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Map Lookup

    Pattern Matching Macros Readability Lower Complexity Performance
  41. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } end
  42. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) end
  43. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do end end
  44. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do flags |> Integer.digits(2) end end
  45. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do flags |> Integer.digits(2) |> Enum.reverse() end end
  46. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do flags |> Integer.digits(2) |> Enum.reverse() |> Enum.zip(@allergies) end end
  47. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.Hash do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do flags |> Integer.digits(2) |> Enum.reverse() |> Enum.zip(@allergies) |> Enum.reduce([], fn {1, {_, allergy}}, acc -> [allergy | acc] _, acc -> acc end) end end
  48. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.BitShifting do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } end
  49. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.BitShifting do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) end
  50. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.BitShifting do @allergies

    %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do Enum.filter_map(@allergies, &(flagged? flags, &1), fn({_, item}) -> item end) end end
  51. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.BitShifting do use

    Bitwise, only_operators: true @allergies %{ 1 => "eggs", 2 => "peanuts", 4 => "shellfish", 8 => "strawberries", 16 => "tomatoes", 32 => "chocolate", 64 => "pollen", 128 => "cats", } def allergic_to?(flags, item), do: item in list(flags) def list(flags) do Enum.filter_map(@allergies, &(flagged? flags, &1), fn({_, item}) -> item end) end defp flagged?(flags, {value, _}) do (flags &&& value) > 0 end end
  52. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) end
  53. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) defp list(<<1 :: size(1), r :: size(7)>>, acc), do: list(<<0 :: size(1), r :: size(7)>>, ["cats"|acc]) end
  54. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) defp list(<<1 :: size(1), r :: size(7)>>, acc), do: list(<<0 :: size(1), r :: size(7)>>, ["cats"|acc]) defp list(<<l :: size(1), 1 :: size(1), r :: size(6)>>, acc), do: list(<<l :: size(1), 0 :: size(1), r :: size(6)>>, ["pollen"|acc]) end
  55. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) defp list(<<1 :: size(1), r :: size(7)>>, acc), do: list(<<0 :: size(1), r :: size(7)>>, ["cats"|acc]) defp list(<<l :: size(1), 1 :: size(1), r :: size(6)>>, acc), do: list(<<l :: size(1), 0 :: size(1), r :: size(6)>>, ["pollen"|acc]) defp list(<<l :: size(2), 1 :: size(1), r :: size(5)>>, acc), do: list(<<l :: size(2), 0 :: size(1), r :: size(5)>>, ["chocolate"|acc]) end
  56. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) defp list(<<1 :: size(1), r :: size(7)>>, acc), do: list(<<0 :: size(1), r :: size(7)>>, ["cats"|acc]) defp list(<<l :: size(1), 1 :: size(1), r :: size(6)>>, acc), do: list(<<l :: size(1), 0 :: size(1), r :: size(6)>>, ["pollen"|acc]) defp list(<<l :: size(2), 1 :: size(1), r :: size(5)>>, acc), do: list(<<l :: size(2), 0 :: size(1), r :: size(5)>>, ["chocolate"|acc]) defp list(<<l :: size(3), 1 :: size(1), r :: size(4)>>, acc), do: list(<<l :: size(3), 0 :: size(1), r :: size(4)>>, ["tomatoes"|acc]) defp list(<<l :: size(4), 1 :: size(1), r :: size(3)>>, acc), do: list(<<l :: size(4), 0 :: size(1), r :: size(3)>>, ["strawberries"|acc]) defp list(<<l :: size(5), 1 :: size(1), r :: size(2)>>, acc), do: list(<<l :: size(5), 0 :: size(1), r :: size(2)>>, ["shellfish"|acc]) defp list(<<l :: size(6), 1 :: size(1), r :: size(1)>>, acc), do: list(<<l :: size(6), 0 :: size(1), r :: size(1)>>, ["peanuts"|acc]) defp list(<<l :: size(7), 1 :: size(1)>>, acc), do: list(<<l :: size(7), 0 :: size(1)>>, ["eggs"|acc]) end
  57. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) defp list(<<1 :: size(1), r :: size(7)>>, acc), do: list(<<0 :: size(1), r :: size(7)>>, ["cats"|acc]) defp list(<<l :: size(1), 1 :: size(1), r :: size(6)>>, acc), do: list(<<l :: size(1), 0 :: size(1), r :: size(6)>>, ["pollen"|acc]) defp list(<<l :: size(2), 1 :: size(1), r :: size(5)>>, acc), do: list(<<l :: size(2), 0 :: size(1), r :: size(5)>>, ["chocolate"|acc]) defp list(<<l :: size(3), 1 :: size(1), r :: size(4)>>, acc), do: list(<<l :: size(3), 0 :: size(1), r :: size(4)>>, ["tomatoes"|acc]) defp list(<<l :: size(4), 1 :: size(1), r :: size(3)>>, acc), do: list(<<l :: size(4), 0 :: size(1), r :: size(3)>>, ["strawberries"|acc]) defp list(<<l :: size(5), 1 :: size(1), r :: size(2)>>, acc), do: list(<<l :: size(5), 0 :: size(1), r :: size(2)>>, ["shellfish"|acc]) defp list(<<l :: size(6), 1 :: size(1), r :: size(1)>>, acc), do: list(<<l :: size(6), 0 :: size(1), r :: size(1)>>, ["peanuts"|acc]) defp list(<<l :: size(7), 1 :: size(1)>>, acc), do: list(<<l :: size(7), 0 :: size(1)>>, ["eggs"|acc]) defp list(_, acc), do: acc end
  58. Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def

    allergic_to?(flags, item), do: item in list(flags) def list(flags), do: list(<<flags>>, []) defp list(<<1 :: size(1), r :: size(7)>>, acc), do: list(<<0 :: size(1), r :: size(7)>>, ["cats"|acc]) defp list(<<l :: size(1), 1 :: size(1), r :: size(6)>>, acc), do: list(<<l :: size(1), 0 :: size(1), r :: size(6)>>, ["pollen"|acc]) defp list(<<l :: size(2), 1 :: size(1), r :: size(5)>>, acc), do: list(<<l :: size(2), 0 :: size(1), r :: size(5)>>, ["chocolate"|acc]) defp list(<<l :: size(3), 1 :: size(1), r :: size(4)>>, acc), do: list(<<l :: size(3), 0 :: size(1), r :: size(4)>>, ["tomatoes"|acc]) defp list(<<l :: size(4), 1 :: size(1), r :: size(3)>>, acc), do: list(<<l :: size(4), 0 :: size(1), r :: size(3)>>, ["strawberries"|acc]) defp list(<<l :: size(5), 1 :: size(1), r :: size(2)>>, acc), do: list(<<l :: size(5), 0 :: size(1), r :: size(2)>>, ["shellfish"|acc]) defp list(<<l :: size(6), 1 :: size(1), r :: size(1)>>, acc), do: list(<<l :: size(6), 0 :: size(1), r :: size(1)>>, ["peanuts"|acc]) defp list(<<l :: size(7), 1 :: size(1)>>, acc), do: list(<<l :: size(7), 0 :: size(1)>>, ["eggs"|acc]) defp list(_, acc), do: acc end
  59. Devon Estes ElixirConf EU 2017 @devoncestes Name ips average deviation

    median pattern matching 3.70 K 270.07 μs ±28.97% 249.00 μs bit shifting 2.02 K 495.19 μs ±29.30% 461.00 μs map 0.62 K 1617.88 μs ±22.87% 1517.00 μs Comparison: pattern matching 3.70 K bit shifting 2.02 K - 1.83x slower map 0.62 K - 5.99x slower
  60. Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit

    Shifting Pattern Matching Readability Lower Complexity Performance
  61. Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit

    Shifting Pattern Matching Readability Lower Complexity Performance
  62. Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit

    Shifting Pattern Matching Readability Lower Complexity Performance
  63. Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit

    Shifting Pattern Matching Readability Lower Complexity Performance
  64. Devon Estes ElixirConf EU 2017 @devoncestes •Lead by example •Talk

    more about your mistakes •More/better tooling LET’S NUDGE!
  65. Devon Estes ElixirConf EU 2017 @devoncestes def search(items, value) do

    search(items, value, 0, tuple_size(items) - 1) end
  66. Devon Estes ElixirConf EU 2017 @devoncestes def search(items, value) do

    search(items, value, 0, tuple_size(items) - 1) end def search(items, value), do: search(items, value, 0, tuple_size(items) - 1)
  67. Devon Estes ElixirConf EU 2017 @devoncestes def search(items, value) do

    search(items, value, 0, tuple_size(items) - 1) end def search(items, value), do: search(items, value, 0, tuple_size(items) - 1) def search(items, value), do: search(items, value, 0, tuple_size(items) - 1)
  68. Devon Estes ElixirConf EU 2017 @devoncestes def search(items, value) do

    search(items, value, 0, tuple_size(items) - 1) end def search(items, value), do: search(items, value, 0, tuple_size(items) - 1) def search(items, value), do: search(items, value, 0, tuple_size(items) - 1) def search(items, value), do: search(items, value, 0, tuple_size(items) - 1)
  69. Devon Estes ElixirConf EU 2017 @devoncestes flags |> Integer.digits(2) |>

    Enum.reverse() |> Enum.zip(@allergies) flags |> Integer.digits(2) |> Enum.reverse() |> Enum.zip(@allergies)
  70. Devon Estes ElixirConf EU 2017 @devoncestes flags |> Integer.digits(2) |>

    Enum.reverse() |> Enum.zip(@allergies) flags |> Integer.digits(2) |> Enum.reverse() |> Enum.zip(@allergies) @allergens |> Enum.with_index() |> Enum.filter_map(&(flagged? flags, &1), fn({item, _}) -> item end)
  71. Devon Estes ElixirConf EU 2017 @devoncestes “WE BEGAN THIS RESEARCH

    WITH THE NOTION THAT USING TYPOGRAPHIC CUES TO HIGHLIGHT IMPORTANT INFORMATION SOURCES IN PROGRAMS WOULD LEAD TO DRAMATIC IMPROVEMENTS IN COMPREHENSION. THE IMPROVEMENTS WE OBSERVED WERE MORE MODEST, DAMPENING OUR ENTHUSIASM. TYPOGRAPHIC SIGNALING HELPS, BUT NOT THAT MUCH. RESEARCH ON TEXT COMPREHENSION AGREES WITH THESE FINDINGS.” DOES SIGNALING HELP PROFESSIONAL PROGRAMMERS READ AND UNDERSTAND COMPUTER PROGRAMS - EDWARD M. GELLENBECK & CURTIS R. COOK
  72. Devon Estes ElixirConf EU 2017 @devoncestes I HAVE NO IDEA

    WHAT I’M TALKING ABOUT, AND NO ONE SHOULD EVER TAKE ME SERIOUSLY, BUT…