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

Fun with Monads

Fun with Monads

Marcelo Piva

July 20, 2019
Tweet

More Decks by Marcelo Piva

Other Decks in Programming

Transcript

  1. Why do we need Monads? We want to use only

    functions. We want to use pure functions. We want to compose functions.
  2. Class Object Instance Inheritance Polymorphism Abstract Factory Builder Factory Method

    Object Pool Prototype Singleton Adapter Bridge Composite Decorator Facade Flyweight Private Class Data Proxy Chain of responsibility Command Interpreter Iterator Mediator Memento Null Object Observer State
  3. Why do we need Monads? We want to use only

    functions. We want to use pure functions. We want to compose functions.
  4. Why do we need Monads? We want to use only

    functions. We want to use pure functions. We want to compose functions.
  5. Imperative public class TransactionRequest { public static String transact(Request request)

    { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } }
  6. Imperative public class TransactionRequest { public static String transact(Request request)

    { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } }
  7. Imperative Functional public class TransactionRequest { public static String transact(Request

    request) { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } } (defn transaction! [transaction] (-> transaction coerce persist! notify!))
  8. Imperative Functional public class TransactionRequest { public static String transact(Request

    request) { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } } (defn transaction! [transaction] (-> transaction coerce persist! notify!)) NullPointerException
  9. Imperative Functional public class TransactionRequest { public static String transact(Request

    request) { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } } (defn transaction! [transaction] (-> transaction coerce persist! notify!)) NullPointerException
  10. What happens when the request is invalid? public class TransactionRequest

    { public static String transact(Request request) { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } }
  11. What happens when the request is invalid? public class TransactionRequest

    { public static String transact(Request request) { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } } Or the transaction is not persisted?
  12. What happens when the request is invalid? Or the transaction

    is not persisted? Or the notification is not sent? public class TransactionRequest { public static String transact(Request request) { Transaction transaction = coerce(request); transaction = persist(transaction); notify(transaction); } }
  13. We need to handle errors public static String transact(Request request)

    { try { Transaction transaction = coerce(request); } catch (Exception e) { return "Error adapting request"; } try { transaction = persist(transaction); if(!transaction) { return "Error persisting transaction"; } } catch(DBError e) { return "DB Error"; } try { if(!notify(transaction)) { return "Error notifying transaction"; } } catch(SMTPError e) { return "SMTP Error"; } return "OK"; }
  14. public static String transact(Request request) { try { Transaction transaction

    = coerce(request); } catch (Exception e) { return "Error adapting request"; } try { transaction = persist(transaction); if(!transaction) { return "Error persisting transaction"; } } catch(DBError e) { return "DB Error"; } try { if(!notify(transaction)) { return "Error notifying transaction"; } } catch(SMTPError e) { return "SMTP Error"; } return "OK"; } We need to handle errors
  15. public static String transact(Request request) { try { Transaction transaction

    = coerce(request); } catch (Exception e) { return "Error adapting request"; } try { transaction = persist(transaction); if(!transaction) { return "Error persisting transaction"; } } catch(DBError e) { return "DB Error"; } try { if(!notify(transaction)) { return "Error notifying transaction"; } } catch(SMTPError e) { return "SMTP Error"; } return "OK"; } We need to handle errors
  16. Before (defn transact! [transaction] (-> transaction coerce persist! notify!)) After

    (defn transact! [transaction] (->= transaction coerce persist! notify!)) We need to handle errors
  17. Before Functional (defn transact! [transaction] (-> transaction coerce persist! notify!))

    After (defn transact! [transaction] (->= transaction coerce persist! notify!)) We need to handle errors
  18. (defn bind [m f] (match m {:error e} e {:ok

    v} (f v))) How bind works?
  19. (defn bind [m f] (match m {:error e} e {:ok

    v} (f v))) How bind works?
  20. (defn bind [m f] (match m {:error e} e {:ok

    v} (f v))) How bind works?
  21. (defn bind [m f] (match m {:error e} e {:ok

    v} (f v))) How bind works?
  22. Now we can compose again (defn transact! [transaction] (let [transaction?

    (bind {:ok transaction} coerce) transaction? (bind transaction? persist!)) transaction? (bind transaction? notify!)] transaction?))
  23. Now we can compose again (defn transact! [transaction] (let [transaction?

    (bind {:ok transaction} coerce) transaction? (bind transaction? persist!) transaction? (bind transaction? notify!)] transaction?))
  24. We’ve just created a Monad, and it’s called Either There

    are others, like: Maybe, IO, State...
  25. We can do better Libs like cats do all the

    hard work https://github.com/funcool/cats And can make our code cleaner cats bind thread (defn transact! [transaction] (->= transaction coerce persist! notify!))
  26. bind :: Either a -> (a -> Either b) ->

    Either b return :: a -> Either a Protocol