Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Micropatterns
Learning to reach for the right tool
Hi! I’m Cameron
(@cameronp)
Time for a story…
October, 2014
BASIC C
Pascal
C++
PythonJava
Ruby
C#
But it didn’t work this
time…
Time passes…
Exercises
• https://projecteuler.net/
• http://adventofcode.com/
• http://exercism.io/
Many Small Projects > One Big Project
And this time it stuck
I was suddenly faster and more
comfortable in Elixir than in any
language I know
What happened?
OTP?
Nope
“Micropatterns”
def categories(num)
categories = []
while categories.length < num do
category = fetch('commerce.department')
categories <<...
Mutation vs Transformation
Mutation vs Transformation
Micropattern #1: Pipelines
“Real World” Example
318,Bulah,Dicki,282,37234
306,Dante,Ankunding,285,23140
317,Monserrat,Mraz,286,35000
303,Cesar,Mann,284,54647
312,Arjun,Zu...
defmodule Phb.Employee do
defstruct [ id: 0,
first_name: "",
last_name: "" ,
salary: 0,
manager: nil
]
end
def load(filename  @default_file) do
filename
|> read
|> parse
|> validate
end
def read(file) do
file
|> File.read!
|> String.split("n", trim: true)
|> Enum.map(&String.split(&1, ","))
end
Enum.map(&String.split(&1, ","))
Micropattern #2: “Strategy”
through higher order functions
Enum.map(&String.split(&1, ","))
Enum.map takes a 1-arity
function
But String.spit/2 is 2-
arity…
What to do?
iex(2)> &String.split(&1, ",")
iex(2)> &String.split(&1, ",")
#Function<6.50752066/1 in :erl_eval.expr/5>
iex(2)> &String.split(&1, ",")
#Function<6.50752066/1 in :erl_eval.expr/5>
iex(3)> splitByCommas = &String.split(&1, ",")
...
iex(2)> &String.split(&1, ",")
#Function<6.50752066/1 in :erl_eval.expr/5>
iex(3)> splitByCommas = &String.split(&1, ",")
...
def read(file) do
file
|> File.read!
|> String.split("n", trim: true)
|> Enum.map(&String.split(&1, ","))
end
iex(7)> Phb.Employee.Loader.load
[["318", "Bulah", "Dicki", "282", "37234"],
["306", "Dante", "Ankunding", "285", "23140"]...
def load(filename  @default_file) do
filename
|> read
|> parse
|> validate
end
def load(filename  @default_file) do
filename
|> read
|> parse
|> validate
end
Micropattern #3: Handling the
“Happy Case” with pattern matching
def parse_record([id_s, fname, lname, mgr_s, salary_s]),
do:
def parse_record([id_s, fname, lname, mgr_s, salary_s]),
do:
%Phb.Employee{
id: to_int(id_s),
first_name: fname,
last_name...
What if there aren’t five
elements in the list?
Well… it’ll crash
Well… it’ll crash
(And maybe that’s ok)
def load(filename  @default_file) do
filename
|> read
|> parse
|> validate
end
How can validation be thought
of in terms of transformation?
defmodule Phb.Employee do
defstruct [ id: 0,
first_name: "",
last_name: "" ,
salary: 0,
manager: nil ]
end
defmodule Phb.Employee do
defstruct [ id: 0,
first_name: "",
last_name: "" ,
salary: 0,
manager: nil,
errors: []]
end
def valid?(%Employee{errors[]}), do: true
def valid?(%Employee{}), do: false
def add_error(%Employee{} = e, error), do:
%Employee{e | errors: [error | e.errors]}
Ok, so we’re ready to
perform validations…
But I’m not going to..
Because there’s too many
micropatterns for one talk!
The #1 thing people report
as difficult:
Recursion.
Recursion.
def categories(num)
categories = []
while categories.length < num do
category = fetch('commerce.department')
categories <<...
def fetch_category, do: :rand.uniform(100)
def fetch_category, do: :rand.uniform(100)
def categories(n), do: categories(n, [])
Recursion rule #1: Think about
the termination case first
def categories(n), do: categories(n, [])
def categories(0, result), do: result
def categories(n), do: categories(n, [])
def categories(0, result), do: result
def categories(n, result) do
new = fetch_ca...
Let’s step back for a
moment
Why are we doing this?
Two reasons
#1: You will become more
comfortable writing Elixir
#2: In Elixir, Micropatterns and
Patterns are the same thing
In OOP, you have objects in the
large, and methods in the small
In FP, we have functions in the
large, and functions in the small
Example: The way we added
errors to an employee struct
Example: The way we added
errors to an employee struct
def add_error(%Employee{} = e, error), do:
%Employee{e | errors: [e...
def add_error(%Employee{} = e, error), do:
%Employee{e | errors: [error | e.errors]}
This is exactly how plugs
work in Phoenix
So if you get comfortable
with these small patterns…
… you’ll find the big architectural
patterns easy to understand
Ok. Let’s try something
harder
318,Bulah,Dicki,282,37234
306,Dante,Ankunding,285,23140
317,Monserrat,Mraz,286,35000
303,Cesar,Mann,284,54647
312,Arjun,Zu...
%Employee{manager: nil,
reports: […]}
%Employee{
reports:[…]}
%Employee{
reports:[…]}
%Employee{
reports:[…]}
%Employee{
r...
Where to begin?
%Employee{manager: nil,
reports: […]}
def add_report(org, new_employee)
def add_report(org, new_employee)
Takes the top of an organization, and a
new employee
def add_report(org, new_employee)
Takes the top of an organization, and a
new employee
and returns either the org with the...
alias Phb.Employee, as: E
def add_report(nil, %E{manager: nil} = report),
do: report
alias Phb.Employee, as: E
def add_report(nil, %E{manager: nil} = report),
do: report
def add_report(nil, _not_a_boss), do:...
alias Phb.Employee, as: E
def add_report(nil, %E{manager: nil} = report),
do: report
def add_report(nil, _not_a_boss), do:...
alias Phb.Employee, as: E
def add_report(nil, %E{manager: nil} = report),
do: report
def add_report(nil, _not_a_boss), do:...
def add_report(m, r) do
new_reports =
m.reports
|> Enum.map(fn rep -> add_report(rep, r) end)
%E{m | reports: new_reports}...
def add_all_reports(reports, org)
def add_all_reports([], org), do: org
def add_all_reports([], org), do: org
def add_all_reports([rep | rest], org) do
def add_all_reports([], org), do: org
def add_all_reports([rep | rest], org) do
case add_report(org, rep) do
def add_all_reports([], org), do: org
def add_all_reports([rep | rest], org) do
case add_report(org, rep) do
^org ->
def add_all_reports([], org), do: org
def add_all_reports([rep | rest], org) do
case add_report(org, rep) do
^org -> add_a...
def add_all_reports([], org), do: org
def add_all_reports([rep | rest], org) do
case add_report(org, rep) do
^org -> add_a...
Easy, right?
No?
Be not afraid
Next steps:
#1: Do small problems.
Use the new patterns
Where to find small problems
• adventofcode.com
• exercism.io
• “Exercises for Programmers”, by Brian
Hogan
• The Little S...
#2: Read the Elixir source, and the
source of the better known Elixir libs
#3: Do it again and again. Mix small
problems in with your big projects
#5: Stay in touch!
I am “cameronp” on:
• twitter
• medium
• github
• gmail
• elixir slack
Lightning Storm: benjaminbenson, on Flickr
Fractal: tommietheturtle, on Flickr
Construction: damienpollet, on Flickr
Photo...
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Micropatterns
Upcoming SlideShare
Loading in …5
×

Micropatterns

570 views

Published on

Slides from my Empex 2016 talk.

Published in: Software
  • Hi there! Get Your Professional Job-Winning Resume Here - Check our website! http://bit.ly/resumpro
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hi there! Get Your Professional Job-Winning Resume Here - Check our website! http://bit.ly/resumpro
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Be the first to like this

Micropatterns

  1. 1. Micropatterns Learning to reach for the right tool
  2. 2. Hi! I’m Cameron (@cameronp)
  3. 3. Time for a story…
  4. 4. October, 2014
  5. 5. BASIC C Pascal C++ PythonJava Ruby C#
  6. 6. But it didn’t work this time…
  7. 7. Time passes…
  8. 8. Exercises • https://projecteuler.net/ • http://adventofcode.com/ • http://exercism.io/
  9. 9. Many Small Projects > One Big Project
  10. 10. And this time it stuck
  11. 11. I was suddenly faster and more comfortable in Elixir than in any language I know
  12. 12. What happened?
  13. 13. OTP?
  14. 14. Nope
  15. 15. “Micropatterns”
  16. 16. def categories(num) categories = [] while categories.length < num do category = fetch('commerce.department') categories << category unless categories. include?(category) end categories end Ruby example
  17. 17. Mutation vs Transformation
  18. 18. Mutation vs Transformation
  19. 19. Micropattern #1: Pipelines
  20. 20. “Real World” Example
  21. 21. 318,Bulah,Dicki,282,37234 306,Dante,Ankunding,285,23140 317,Monserrat,Mraz,286,35000 303,Cesar,Mann,284,54647 312,Arjun,Zulauf,286,37397 297,Ariel,Wisoky,284,31875 309,Herminia,Heaney,285,50981 310,Terrence,Macejkovic,285,38499 287,Abbigail,Miller,282,23391 282,Lavon,Kshlerin,281,46101 290,Joelle,Abbott,282,35470 . . .
  22. 22. defmodule Phb.Employee do defstruct [ id: 0, first_name: "", last_name: "" , salary: 0, manager: nil ] end
  23. 23. def load(filename @default_file) do filename |> read |> parse |> validate end
  24. 24. def read(file) do file |> File.read! |> String.split("n", trim: true) |> Enum.map(&String.split(&1, ",")) end
  25. 25. Enum.map(&String.split(&1, ","))
  26. 26. Micropattern #2: “Strategy” through higher order functions
  27. 27. Enum.map(&String.split(&1, ","))
  28. 28. Enum.map takes a 1-arity function
  29. 29. But String.spit/2 is 2- arity…
  30. 30. What to do?
  31. 31. iex(2)> &String.split(&1, ",")
  32. 32. iex(2)> &String.split(&1, ",") #Function<6.50752066/1 in :erl_eval.expr/5>
  33. 33. iex(2)> &String.split(&1, ",") #Function<6.50752066/1 in :erl_eval.expr/5> iex(3)> splitByCommas = &String.split(&1, ",") #Function<6.50752066/1 in :erl_eval.expr/5>
  34. 34. iex(2)> &String.split(&1, ",") #Function<6.50752066/1 in :erl_eval.expr/5> iex(3)> splitByCommas = &String.split(&1, ",") #Function<6.50752066/1 in :erl_eval.expr/5> iex(4)> splitByCommas.("1,2,3") ["1", "2", "3"]
  35. 35. def read(file) do file |> File.read! |> String.split("n", trim: true) |> Enum.map(&String.split(&1, ",")) end
  36. 36. iex(7)> Phb.Employee.Loader.load [["318", "Bulah", "Dicki", "282", "37234"], ["306", "Dante", "Ankunding", "285", "23140"], ["317", "Monserrat", "Mraz", "286", "35000"], ["303", "Cesar", "Mann", "284", "54647"], ["312", "Arjun", "Zulauf", "286", "37397"],
  37. 37. def load(filename @default_file) do filename |> read |> parse |> validate end
  38. 38. def load(filename @default_file) do filename |> read |> parse |> validate end
  39. 39. Micropattern #3: Handling the “Happy Case” with pattern matching
  40. 40. def parse_record([id_s, fname, lname, mgr_s, salary_s]), do:
  41. 41. def parse_record([id_s, fname, lname, mgr_s, salary_s]), do: %Phb.Employee{ id: to_int(id_s), first_name: fname, last_name: lname, manager: to_int(mgr_s), salary: to_int(salary_s) }
  42. 42. What if there aren’t five elements in the list?
  43. 43. Well… it’ll crash
  44. 44. Well… it’ll crash (And maybe that’s ok)
  45. 45. def load(filename @default_file) do filename |> read |> parse |> validate end
  46. 46. How can validation be thought of in terms of transformation?
  47. 47. defmodule Phb.Employee do defstruct [ id: 0, first_name: "", last_name: "" , salary: 0, manager: nil ] end
  48. 48. defmodule Phb.Employee do defstruct [ id: 0, first_name: "", last_name: "" , salary: 0, manager: nil, errors: []] end
  49. 49. def valid?(%Employee{errors[]}), do: true def valid?(%Employee{}), do: false
  50. 50. def add_error(%Employee{} = e, error), do: %Employee{e | errors: [error | e.errors]}
  51. 51. Ok, so we’re ready to perform validations…
  52. 52. But I’m not going to..
  53. 53. Because there’s too many micropatterns for one talk!
  54. 54. The #1 thing people report as difficult:
  55. 55. Recursion.
  56. 56. Recursion.
  57. 57. def categories(num) categories = [] while categories.length < num do category = fetch('commerce.department') categories << category unless categories. include?(category) end categories end Ruby example
  58. 58. def fetch_category, do: :rand.uniform(100)
  59. 59. def fetch_category, do: :rand.uniform(100) def categories(n), do: categories(n, [])
  60. 60. Recursion rule #1: Think about the termination case first
  61. 61. def categories(n), do: categories(n, []) def categories(0, result), do: result
  62. 62. def categories(n), do: categories(n, []) def categories(0, result), do: result def categories(n, result) do new = fetch_category case (new in result) do true -> categories(n, result) false -> categories(n - 1, [new | result]) end end
  63. 63. Let’s step back for a moment
  64. 64. Why are we doing this?
  65. 65. Two reasons
  66. 66. #1: You will become more comfortable writing Elixir
  67. 67. #2: In Elixir, Micropatterns and Patterns are the same thing
  68. 68. In OOP, you have objects in the large, and methods in the small
  69. 69. In FP, we have functions in the large, and functions in the small
  70. 70. Example: The way we added errors to an employee struct
  71. 71. Example: The way we added errors to an employee struct def add_error(%Employee{} = e, error), do: %Employee{e | errors: [error | e.errors]}
  72. 72. def add_error(%Employee{} = e, error), do: %Employee{e | errors: [error | e.errors]}
  73. 73. This is exactly how plugs work in Phoenix
  74. 74. So if you get comfortable with these small patterns…
  75. 75. … you’ll find the big architectural patterns easy to understand
  76. 76. Ok. Let’s try something harder
  77. 77. 318,Bulah,Dicki,282,37234 306,Dante,Ankunding,285,23140 317,Monserrat,Mraz,286,35000 303,Cesar,Mann,284,54647 312,Arjun,Zulauf,286,37397 297,Ariel,Wisoky,284,31875 309,Herminia,Heaney,285,50981 310,Terrence,Macejkovic,285,38499 287,Abbigail,Miller,282,23391 282,Lavon,Kshlerin,281,46101 290,Joelle,Abbott,282,35470 . . .
  78. 78. %Employee{manager: nil, reports: […]} %Employee{ reports:[…]} %Employee{ reports:[…]} %Employee{ reports:[…]} %Employee{ reports:[…]} %Employee{ reports:[…]} %Employee{ reports:[…]} %Employee{ reports:[…]
  79. 79. Where to begin?
  80. 80. %Employee{manager: nil, reports: […]}
  81. 81. def add_report(org, new_employee)
  82. 82. def add_report(org, new_employee) Takes the top of an organization, and a new employee
  83. 83. def add_report(org, new_employee) Takes the top of an organization, and a new employee and returns either the org with the employee added… or the org unchanged
  84. 84. alias Phb.Employee, as: E def add_report(nil, %E{manager: nil} = report), do: report
  85. 85. alias Phb.Employee, as: E def add_report(nil, %E{manager: nil} = report), do: report def add_report(nil, _not_a_boss), do: nil
  86. 86. alias Phb.Employee, as: E def add_report(nil, %E{manager: nil} = report), do: report def add_report(nil, _not_a_boss), do: nil def add_report(%E{id: m_id} = m, %E{manager: m_id} = r) do %E{m | reports: [r | m.reports]} end
  87. 87. alias Phb.Employee, as: E def add_report(nil, %E{manager: nil} = report), do: report def add_report(nil, _not_a_boss), do: nil def add_report(%E{id: m_id} = m, %E{manager: m_id} = r) do %E{m | reports: [r | m.reports]} end def add_report(m, r) do new_reports = m.reports |> Enum.map(fn rep -> add_report(rep, r) end) %E{m | reports: new_reports} end
  88. 88. def add_report(m, r) do new_reports = m.reports |> Enum.map(fn rep -> add_report(rep, r) end) %E{m | reports: new_reports} end
  89. 89. def add_all_reports(reports, org)
  90. 90. def add_all_reports([], org), do: org
  91. 91. def add_all_reports([], org), do: org def add_all_reports([rep | rest], org) do
  92. 92. def add_all_reports([], org), do: org def add_all_reports([rep | rest], org) do case add_report(org, rep) do
  93. 93. def add_all_reports([], org), do: org def add_all_reports([rep | rest], org) do case add_report(org, rep) do ^org ->
  94. 94. def add_all_reports([], org), do: org def add_all_reports([rep | rest], org) do case add_report(org, rep) do ^org -> add_all_reports(rest ++ [rep], org)
  95. 95. def add_all_reports([], org), do: org def add_all_reports([rep | rest], org) do case add_report(org, rep) do ^org -> add_all_reports(rest ++ [rep], org) new_org -> add_all_reports(rest, new_org) end end
  96. 96. Easy, right?
  97. 97. No?
  98. 98. Be not afraid
  99. 99. Next steps:
  100. 100. #1: Do small problems. Use the new patterns
  101. 101. Where to find small problems • adventofcode.com • exercism.io • “Exercises for Programmers”, by Brian Hogan • The Little Schemer, Friedman and Felleisen
  102. 102. #2: Read the Elixir source, and the source of the better known Elixir libs
  103. 103. #3: Do it again and again. Mix small problems in with your big projects
  104. 104. #5: Stay in touch! I am “cameronp” on: • twitter • medium • github • gmail • elixir slack
  105. 105. Lightning Storm: benjaminbenson, on Flickr Fractal: tommietheturtle, on Flickr Construction: damienpollet, on Flickr Photo Credits: © 2016, Cameron Price

×