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

Functional Lessons Learnt

Functional Lessons Learnt

The story of my experience learning Clojure and Elixir, and overall the functional paradigm.

Avatar for Javier García

Javier García

October 27, 2019
Tweet

More Decks by Javier García

Other Decks in Programming

Transcript

  1. Agenda Love at first sight? Changing the mindset Technicalities Keeping

    functions pure Geometrical readability Don't reduce reducers Encapsulate data with functions 2
  2. Love at first sight? Not How do I store some

    shared mutable state? How do I mock stuff? How does dependency injection work? ... 3
  3. Bruce Lee Empty your cup so that it may be

    filled; become devoid to gain totality. “ “ 5
  4. Technicalities "Whatever the truth is, I don't see how it

    will help me get food on the table." - Suzanne Collins 8
  5. Impurity as lack of immutability var carPark = new CarPark();

    var porsche = new Car("Porsche"); carPark.open(); carPark.park(porsche); carPark.unpark(porsche); carPark.close(); 10
  6. Embracing immutability for a purer world CarPark park = new

    CarPark(); CarPark openPark = park.open(); CarPark parkWithPorsche = openPark.park("Porsche"); CarPark parkWithoutPorsche = openPark.unpark("Porsche"); CarPark finalPark = parkWithoutPorsche.close(); Where, for example, CarPark::park looks like: public CarPark open() { return new CarPark("Open", this.cars); } 11
  7. Very similarly in Elixir CarPark.new() |> CarPark.open() |> CarPark.park("porsche") |>

    CarPark.unpark("porsche") |> CarPark.close() But elixir comes with immutability out of the box :) 12
  8. But purity is so much more than immutability Logging HTTP

    Requests Calling your agent [Insert your fav here] 13
  9. Horizontal def save_contact(name, params) do with {:ok, contact} <- find(name),

    {:ok, saved_contact} <- save(contact, params), :ok <- notify_owner(saved_contact) do {:ok, saved_contact} end end 16
  10. Vertical def process(document_title) do document_title |> load() |> format_content() |>

    format_metadata() |> convert_to_pdf() |> upload(:google-drive) end 17
  11. Not this def process(document_title) do {:ok, document} = load(document_title) pdf

    = document |> format_content() |> format_metadata() |> convert_to_pdf() upload(:google-drive, pdf) end 18
  12. One last example from Londibot defp to_subscriptions_message(subscriptions) do lines =

    subscriptions |> Enum.map(fn x -> Map.get(x, :tfl_lines) end) |> List.flatten() |> Enum.join(", ") if String.length(lines) > 0 do "You are currently subscribed to: #{lines}" else "You are currently not subscribed to any line" end end 19
  13. # Vertical function defp to_subscriptions_message(subscriptions) do subscriptions |> Enum.map(fn x

    -> Map.get(x, :tfl_lines) end) |> List.flatten() |> Enum.join(", ") |> subscriptions_message_text() end # Horizontal functions defp subscriptions_message_text(""), do: "You are currently not subscribed to any line" defp subscriptions_message_text(lines), do: "You are currently subscribed to: #{lines}" 20
  14. What is a reducer? a reducer is simply a function

    that accepts some data, applies some transformations to it, and returns the modified version of the data “ “ 22
  15. We can wrap it in a struct def capitalize(%SomeStruct{value: value}

    = s) do %SomeStruct{s | value: String.capitalize(s)} end 24
  16. Then we can call its siblings... def hash(%SomeStruct{value: value} =

    s) do hashed = :crypto.hash(:sha, value) |> Base.encode16 %SomeStruct{s | value: hashed} end def downcase(%SomeStruct{value: value} = s) do %SomeStruct{s | value: String.downcase(s)} end 25
  17. Thus allowing us to... iex> %SomeStruct{value: "javier"} |> capitalize() |>

    hash() |> downcase() %SomeStruct{value: "7f3d0970ec0e336aa08a9e14d4d88e79131e0065"} 26
  18. Lastly, if anybody's fond of testing... def assert_result(%SomeStruct{value: v} =

    s, expected) do assert v == expected s end test "complex hashing works!" do %SomeStruct{value: "javier"} |> capitalize() |> assert_result("Javier") |> hash() |> assert_result("7F3D0970EC0E336AA08A9E14D4D88E79131E0065") |> downcase() |> assert_result("7f3d0970ec0e336aa08a9e14d4d88e79131e0065") end 27
  19. A common pattern in OOP public class Car { private

    int wheels; public Car(int wheels) { this.wheels = wheels; } public String getWheels() { return wheels; } } 29
  20. In Clojure (defn new-job [userid cronexpression service] {:userid userid :cronexpression

    cronexpression :service service}) (defn get-id [job] (:id job)) (defn get-cron-expr [job] (:cronexpression job)) (defn get-user-id [job] (:userid job)) 30