Monads Author: Gal Lalouche - Technion 2017 © 1 1.

Slides:



Advertisements
Similar presentations
Java 8 Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author: Assaf Israel - Technion 2013.
Advertisements

Language Issues for Inheritance CS 5010 Program Design Paradigms "Bootcamp" Lesson 12.3 © Mitchell Wand, This work is licensed under a Creative.
CS 206 Introduction to Computer Science II 01 / 21 / 2009 Instructor: Michael Eckmann.
Monads Technion – Institute of Technology Software Design (236700) Author: Gal Lalouche - Technion 2015 © 1.
Java 8 Lambda expressions and you Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author:
07 Coding Conventions. 2 Demonstrate Developing Local Variables Describe Separating Public and Private Members during Declaration Explore Using System.exit.
Exceptions Syntax, semantics, and pragmatics Exceptions1.
SWE 619 © Paul Ammann Procedural Abstraction and Design by Contract Paul Ammann Information & Software Engineering SWE 619 Software Construction cs.gmu.edu/~pammann/
Java Threads. What is a Thread? A thread can be loosely defined as a separate stream of execution that takes place simultaneously with and independently.
Dependency Injection Technion – Institute of Technology Author: Gal Lalouche - Technion 2015 ©
Best Practices. Contents Bad Practices Good Practices.
1 Inheritance. 2 Why use inheritance?  The most important aspect of inheritance is that it expresses a relationship between the new class and the base.
More About Objects and Methods Chapter 5. Outline Programming with Methods Static Methods and Static Variables Designing Methods Overloading Constructors.
0 Functors in Haskell Adapted from material by Miran Lipovaca.
CS2110: SW Development Methods Inheritance in OO and in Java Part 2: Topics: Forms of inheritance Interfaces in Java.
0 Odds and Ends in Haskell: Folding, I/O, and Functors Adapted from material by Miran Lipovaca.
ICS3U_FileIO.ppt File Input/Output (I/O)‏ ICS3U_FileIO.ppt File I/O Declare a file object File myFile = new File("billy.txt"); a file object whose name.
(c) University of Washington10-1 CSC 143 Java Errors and Exceptions Reading: Ch. 15.
Exceptions Lecture 11 COMP 401, Fall /25/2014.
Spartan Programming Technion – Institute of Technology Author: Gal Lalouche - Technion 2016 © 1 “Minimalism isn't always the right choice, but it's.
Java 8 Lambda Expressions and You Technion – Institute of Technology Software Design (236700) Based on slides by: Sagie Davidovich, Assaf Israel Author:
Introduction to Exceptions in Java CS201, SW Development Methods.
CSCE 240 – Intro to Software Engineering Lecture 3.
Monads Technion – Institute of Technology Software Design (236700) Author: Gal Lalouche - Technion 2016 © 1.
Test Isolation and Mocking Technion – Institute of Technology Author: Gal Lalouche © 1 Author: Gal Lalouche - Technion 2016 ©
Java 8, Lambda Expressions and You
MPCS – Advanced java Programming
Functional Programming and Stream API
Test Isolation and Mocking
Introduction to Computer Science / Procedural – 67130
Inheritance and Polymorphism
Java Programming Language
Inheritance & Polymorphism
CPSC 315 – Programming Studio Spring 2012
Syntax, semantics, and pragmatics
Type Systems Terms to learn about types: Related concepts: Type
Inheritance "Question: What is the object oriented way of getting rich? Answer: Inheritance.“ “Inheritance is new code that reuses old code. Polymorphism.
Functional Programming with Java
CMPE212 – Stuff… Exercises 4, 5 and 6 are all fair game now.
PROGRAMMING IN HASKELL
Strings, Line-by-line I/O, Functions, Call-by-Reference, Call-by-Value
Exception Handling Chapter 9.
Learning to Program in Python
Java Programming Language
Portability CPSC 315 – Programming Studio
Packages and Interfaces
PROGRAMMING IN HASKELL
CSE341: Programming Languages Lecture 12 Equivalence
Functional interface.
Fall 2018 CISC124 2/24/2019 CISC124 Quiz 1 marking is complete. Quiz average was about 40/60 or 67%. TAs are still grading assn 1. Assn 2 due this Friday,
Introduction to Spark.
CS5220 Advanced Topics in Web Programming More Node.js
Computer Science 312 What’s New in Java 8? 1.
CMPE212 – Reminders Assignment 3 due next Friday.
Interfaces.
CISC101 Reminders Assignment 3 due next Friday. Winter 2019
Polymorphism 2019/4/29.
Designing For Testability
CSC 143 Java Errors and Exceptions.
Exceptions 10-May-19.
Winter 2019 CMPE212 5/25/2019 CMPE212 – Reminders
CMPE212 – Reminders Assignment 2 due next Friday.
slides created by Ethan Apter
CSE341: Programming Languages Lecture 12 Equivalence
Exception Handling.
„Lambda expressions, Optional”
Exceptions and networking
Promises.
Presentation transcript:

Monads Author: Gal Lalouche - Technion 2017 © 1 1

Reminder: composing Streams Old imperative code: New Stream API code: List<Student> students = new ArrayList<>() for (Country c : getAllCountries()) { for (Univerisity u : c.getUniversities()) { for (Faculty f : u.getFaculties()) { for (Student s : f.getStudents()) { students.add(s) } Author: Gal Lalouche - Technion 2017 © List<Student> students = getAllCountries().stream() .flatMap(c -> c.getUniversities().stream()) .flatMap(u -> u.getFaculties().stream()) .flatMap(f -> f.getStudents().stream()) .collect(Collectors.toList()); This increasing indent is called the "Pyramid of Doom"

Reminder II: composing Optionals Old imperative code: New Optional API code (assuming everything returned an Optional instead of nullable): // working with nulls Student s = getStudent(); if (s != null) { Course c = s.getCourse("Software Design"); if (c != null) { Exam e = c.getMoedA(); if (e != null) { return e.getGrade(); } return null; Author: Gal Lalouche - Technion 2017 © This could be made shorter by using early exits, but it's written this way to show the creeping pyramid There are several problems with replacing this with a try-catch: We might catch the wrong exception Exceptions are several orders of magnitude slower This composition doesn't necessarily happen in the same place return getStudent() .flatMap(x -> x.getCourse("Software Design")) .flatMap(Course::getMoedA) .flatMap(Moed::getGrade);

flatMap in detail Why do we need flatMap? If we used map, we would get a Stream of Stream: map doesn't compose flatMap performs a map operation, followed by a "flatten" operation, converting a Stream<Stream<S>> into Stream<S>: Composes easily Both Stream and Optional are something called a Monad Stream<Stream<Faculties>> = univerisities.stream() .map(u -> u.getFaculties().stream()); Author: Gal Lalouche - Technion 2017 © <S> Stream<S> flatMap(Function<T, Stream<S>> f); roll credits

What is a Monad? 5 A monad is any class that exposes the following API: Unlike other design patterns we saw, monads don't reduce coupling, increase cohesion, or use polymorphism (in Java) Rather, Monads give us: Shorter and more declarative code A uniform way of composing functionality Generic containers that provide additional explicit context The ability to focus on the happy path while taking care of the unhappy paths class MyMonad<T> { public static <T> MyMonad<T> of(T t); public <S> MyMonad<S> flatMap(Function<T, MyMonad<S>> f); } Author: Gal Lalouche - Technion 2017 © Wildcards omitted for brevity The actual argument to flatMap is Function<? super T, ? extends MyMonad<S>> 5

The Monad laws A proper monad has to satisfy the three monadic laws: 6 A proper monad has to satisfy the three monadic laws: Left Identity: MyMonad.of(x).flatMap(f) === f.apply(x) Right Identity: m.flatMap(x -> MyMonad.of(x)) === m Associativity: m.flatMap(f).flatMap(g) === m.flatMap(x -> f.apply(x).flatMap(g)) Author: Gal Lalouche - Technion 2017 © These are the exact laws of a monoid, where of is the identity element, and flatMap is the binary operation 6

The Box monad The simplest monad is just a box: While obviously not very useful, most monads are actually a box with a few additional features Optional is a box that can be empty Stream is a box that can contain multiple elements class Box<T> { private final T t; private Box(T t) { this.t = t; } public static <T> Box<T> of(T t) { return new Box(t); public <S> Box<S> flatMap(Function<T, Box<S>> f) { return f.apply(t); Author: Gal Lalouche - Technion 2017 © 7

Optional: Implementation 8 Example implementation of Optional: class Optional<T> { private final T element; private Optional(T element) { this.element = element; } // monadic methods public static <T> Optional<T> of(T t) { if (t == null) throw new IllegalArgumentException("Use Optional.empty()"); return new Optional(t); public <S> Optional<S> flatMap(Function<T, Optional<S>> f) { return element == null ? this : f.apply(element); // not part of the monadic interface public static <T> Optional<T> empty() { return new Optional<T>(null); Author: Gal Lalouche - Technion 2017 © 8

map and forEach 9 What about map, forEach, and other higher-order functions? flatMap and of is enough to implement these and others: map and forEach let us focus on the happy path, while taking care of the ugly details inside the monad class MyMonad<T> { public <S> MyMonad<S> map(Function<T, S> f) { return flatMap(t -> of(f.apply(t))); } public MyMonad<T> forEach(Consumer<T> f) { return map(t -> { f.accept(t); return t; }); Author: Gal Lalouche - Technion 2017 © This is part of the reason why we need the monad laws: to ensure that this work as expected forEach is called ifPresent in Optional 9

The Future monad: Motivation 10 Suppose we have a task that takes a very long time: How should handle this? Blocking until all tasks are complete is sometimes unacceptable For example, in a user interface List<Picture> getAlbum(String name) { // fetches data from remote server… Profile p = getProfile(name); List<Album> as = getAlbums(p); // fetches data … Album a = find(as, "Thailand"); // fetches data … return getPictures(a); // fetches data … } Author: Gal Lalouche - Technion 2017 © 10

Future: Alternatives I 11 Running requests in their own thread: Problems? Doesn't compose Handling threads is annoying Not declarative enough Hard to wait for a return value new Thread(() -> { Profile p = getProfile(name); List<Album> as = getAlbums(p); Album a = find(as, "Thailand"); return getPictures(a); }); Author: Gal Lalouche - Technion 2017 © \ 11

Future: Alternatives II 12 The standard way of handling asynchronisity is to use callbacks: Problems? Still annoying to compose (though less so than threads) Still hard to get a return value When we want to compose containers/abstractions of generic types, we want Monads! void getAlbum(String name, Consumer<List<Picture>> callback) { new Thread(() -> callback.accept(getAlbum(name))).start(); } Author: Gal Lalouche - Technion 2017 © callback1(params1, t1 -> callback2(params2, t2 -> callback3(params3, t3 -> …) // this is often called the pyramid of doom..) ); There's that pyramid again! 12

Future: Class definition 13 We want a generic container for a promise of an element That is, a box that will, sometime in the future, contain an object We call this container Future class Future<T> { /* monad functions */ static <T> Future<T> of(T t) { … } <S> Future<S> flatMap(Function<T, Future<S>> f) { … } /* monadic extensions */ <S> Future<S> map(Function<T, S> f) { … } Future<T> forEach(Consumer<T> c) { … } /* non-monadic functions */ // lazily creates a future on a new thread static <T> Future<T> of(Supplier<T> s) { … } // blocks until a value is available (avoid if possible) T get() throws InterruptedException { … } } Author: Gal Lalouche - Technion 2017 © Notice that we don't care about the Future's implementation, just its interface 13

Future: Solution Replace old code with future-aware code 14 Replace old code with future-aware code Since Future is a monad, composition is trivial // old code Profile getProfile() { … } // Future-aware code Future<Profile> getProfile() { … } // same for all the other functions Future<List<Picture>> getThailandAlbum(String userName) { return getProfile(name) .flatMap(profile -> getAlbums(profile)) .flatMap(albums -> find(albums, "Thailand")) .flatMap(album -> getPictures(album)); } Author: Gal Lalouche - Technion 2017 © 14

Future: Callbacks and lifts What about a callback? That's exactly the default forEach implementation Reminder: Usage in Future: We "lift" non-monadic functions/methods by using map: public Future<T> forEach(Consumer<T> f) { map(t -> {f.accept(t); t;}); } getThailandAlbum("John smith") .forEach(GUI::displayPictures); Author: Gal Lalouche - Technion 2017 © class Album { DateTime getCreationTime() { … } } Future<Album> album = getAlbum(profile); Future<DateTime> creationTime = album.map(Album::getCreationTime);

Future: CompletableFuture<T> The standard library's monadic future implementation is CompletableFuture Not to be confused with FutureTask, which is an old (1.5) class that is not monadic Both futures implement Future, which is also not monadic Unlike Stream and Option, which use the standard monad names, it has its own unique names: of => completableFuture flatMap => thenCompose map => thenApply forEach => thenAccept Many others Author: Gal Lalouche - Technion 2017 ©

Monad diagram We prefer to program in the happy path (bottom row in diagram) But we can't due to additional concerns Function might fail, take a lot of time to compute, etc. We have to program under the monadic context (top row) Monads wrap our elements with classes that address these concerns Author: Gal Lalouche - Technion 2017 ©

Other monads There are other kinds of interesting monads: Virtually any Collection can adapted to a monad For some pathological cases, Set isn't a monad, i.e., doesn't respect the three monad laws Observer<T> can also be monadic For an example implementation, see RxJava Future is the promise of a single element; Observer is the promise of many (or zero) elements Same relationship as Optional vs. Stream IO is used in pure languages like Haskell to abstract side-effects and IO operations (user input, reading files, etc.) Try is used in languages without checked exception to abstract errors More general than Optional Can also be used in Java since it's more composable than checked exceptions Author: Gal Lalouche - Technion 2017 ©

Monad: Applicability Use Monads when you want to: Wrap your types with an explicit context/caveat While still being able to use existing functions with map or expose the wrapped element with forEach Write more declarative code We focus on the happy path, while taking care of any additional details inside the monad Provide a uniform API for generic composition Author: Gal Lalouche - Technion 2017 ©

Monad: Implications Easy to compose Context and other assumptions are explicit Types are better than comments "Scary" for beginners But what they don't know won't hurt them Java's type system forces us to perform a lot of code duplication Can't write generic code that works on any monad Have to re-implement map, forEach for every monad But you probably don't want to use the default implementation anyway Author: Gal Lalouche - Technion 2017 ©

Further Reading cdsmith.wordpress.com/2012/04/18/why-do-monads-matter/ (Language Agnostic / Haskellish) wiki.haskell.org/All_About_Monads (Haskell) learnyouahaskell.com/a-fistful-of-monads (Haskell) www.coursera.org/course/reactive (Scala) github.com/ReactiveX/RxJava (Java) http://blog.plover.com/prog/burritos.html (Burritos) Author: Gal Lalouche - Technion 2017 ©

Implementations, additive monads, fluent APIs and further reading Author: Gal Lalouche - Technion 2017 © Appendix

Fluent APIs In object-oriented languages, monadic transformations are chained in the order that they are evaluated: Examples include Java’s Stream, C# LINQ (with IEnumerable), Ruby’s Enumerable module, and even bash piping. Without fluent APIs, we have to read and invoke functions in reverse order: Languages with non-fluent API: ML, Haskell, Python, C++, Lisp(s) int sum = stream .map(x -> x * x) .filter(x -> x % 3 == 0) .map(x -> x + 8) .sum() Author: Gal Lalouche - Technion 2017 © int sum = sum( map(x -> x + 8, filter(x -> x % 3 == 0, map(x -> x * x, list))));

Fluent APIs (cont.) Often enough, any API that returns this from method calls is called Fluent This pattern is especially common with builder objects But unlike monads, it doesn’t reverse the order of reading: vs. It’s possible to transform non-fluent APIs to fluent APIs either with wrapping / decorators, or special functions (e.g., in Haskell/F#/ML: x |> f = f x) new Builder() .setX(x) .setY(y) .build(); Author: Gal Lalouche - Technion 2017 © Builder b = new Builder() b.setX(x) b.setY(y) b.build();

Monads in Haskell/Scala In languages with a richer type-system than Java, like Haskell or Scala, Monads are much more interesting/useful This is because you can write code that works on a general Monad For example, in our java implementation we have to implement map, forEach, filter, etc. for every new Monad class we define If we had higher kinded types, we could implement it just once for all monads Author: Gal Lalouche - Technion 2017 ©