Functional programming languages promise to be easier to test and easier to debug. However, when learning the functional way we often try to translate our current techniques to another language. This is usually not easy and the end result is far from those promises we've heard. Early frustrations might even discourage from further learning.
In this talk I will show you two very simple patterns:
- Designing code around single data structure
- Dealing with impure parts of program like DBs, external services or IO
This should give beginners jump start for their first toy projects and further exploration.
15. Patterns
1. Use single data structure that is your single
source of truth and many small functions
operating on it.
2. Separate pure and impure parts.
27. Data validation problem
● Current value:
%User{name: „Tom”, age: 29}
● Things that we want to change:
%{age: 30}
● Is the data valid? If not – why?
43. Benefits
● Easy to compose: just |> another validator
● Easy to extend with custom validators
● Easy to test: all functions are pure
● Bonus: it works for any data.
45. Immutability digression
● Creating new structure every time is optimized
when language provides immutability
● list = [1, 2, 3]
● list2 = [0 | list]
0
1 2 3
46. Immutability digression
● Creating new structure every time is optimized
when language provides immutability
● list = [1, 2, 3]
● list2 = [0 | list]
● list3 = [4 | list]
0
4
1 2 3
51. Benefits
● Easy to compose: just |> another operation
● Easy to extend with Multi.run
● Easy to test with Multi.to_list
52. Garbage Collection digression
● Erlang GC is run separately for all processes
● When process dies, all its memory is freed
● This means that, if
– you use a process per request in your web
application and
– it has short lifecycle
The garbage collection may never happen!
53. Plug (or what makes Phoenix cool)
1. A specification for composable modules
between web applications
2. Connection adapters for different web
servers in the Erlang VM
56. Pipeline
● Pipeline is a set of plugs
pipeline :pipeline_name do
plug :plug1
plug :plug2
end
● Pipeline is a plug
conn conn conn
pipeline
conn conn conn
v1
plug
v2
57. Almost the same...
def pipeline_name(conn) do
conn
|> if_not_halted(plug1)
|> if_not_halted(plug2)
end
63. Benefits
● Easy to compose: set of plugs is a plug
● Easy to extend: your own plugs can be put
anywhere in the request cycle
● Easy to test: but you need to ensure explicit
contracts