Share
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
Transcript
103 slides
-
Devon Estes ElixirConf EU 2017 @devoncestes REFACTORING ELIXIR LESSONS LEARNED
FROM A YEAR ON EXERCISM.IO -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Easier
to change WHY REFACTOR? -
Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Easier
to change •Lower complexity WHY REFACTOR? -
Devon Estes ElixirConf EU 2017 @devoncestes •Easier to read/understand •Easier
to change •Lower complexity •Increase performance WHY REFACTOR? -
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? -
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? -
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? -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.Cond do def
search(items, value) do end end -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes defmodule BinarySearch.PatternMatching do def
search(items, value), do: search(items, value, 0, tuple_size(items) - 1) end -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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) -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching
Cond Statement Readability Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching
Cond Statement Readability Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching
Cond Statement Readability Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes BINARY SEARCH Pattern Matching
Cond Statement Readability Complexity Performance -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Pattern Matching
Map Lookup Macros Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Pattern Matching
Map Lookup Macros Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Pattern Matching
Map Lookup Macros Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes SPACE AGE Map Lookup
Pattern Matching Macros Readability Lower Complexity Performance -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes defmodule Allergies.PatternMatching do def
allergic_to?(flags, item), do: item in list(flags) end -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit
Shifting Pattern Matching Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit
Shifting Pattern Matching Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit
Shifting Pattern Matching Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes ALLERGIES Map Lookup Bit
Shifting Pattern Matching Readability Lower Complexity Performance -
Devon Estes ElixirConf EU 2017 @devoncestes ECONOMICS IS THE STUDY
OF CHOICE IN A WORLD OF SCARCITY -
Devon Estes ElixirConf EU 2017 @devoncestes •Lead by example •Talk
more about your mistakes LET’S NUDGE! -
Devon Estes ElixirConf EU 2017 @devoncestes •Lead by example •Talk
more about your mistakes •More/better tooling LET’S NUDGE! -
Devon Estes ElixirConf EU 2017 @devoncestes def search(items, value) do
search(items, value, 0, tuple_size(items) - 1) end -
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) -
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) -
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) -
Devon Estes ElixirConf EU 2017 @devoncestes flags |> Integer.digits(2) |>
Enum.reverse() |> Enum.zip(@allergies) -
Devon Estes ElixirConf EU 2017 @devoncestes flags |> Integer.digits(2) |>
Enum.reverse() |> Enum.zip(@allergies) flags |> Integer.digits(2) |> Enum.reverse() |> Enum.zip(@allergies) -
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) -
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 -
Devon Estes ElixirConf EU 2017 @devoncestes I HAVE NO IDEA
WHAT I’M TALKING ABOUT, AND NO ONE SHOULD EVER TAKE ME SERIOUSLY, BUT… -
Devon Estes ElixirConf EU 2017 @devoncestes DON’T MAKE ME THINK
ABOUT FORMATTING -
Devon Estes ElixirConf EU 2017 @devoncestes BETTER YET, LET A
TOOL DO IT FOR ME