monads in swift

Post on 22-Jan-2018

1.244 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Monads

Functional Programming

First-class and higher-order functionslet double: (Int) -> Int = { $0 * 2 }

func apply(value: Int, function: (Int) -> Int) -> Int { return function(value) }

let result = apply(value: 4, function: double) // result == 8

Some properties• A function is said to be pure if it produces no side-effects and its

return value only depends on its arguments

• An expression is said to be referentially transparent if it can be replaced by its value without altering the program’s behavior

Optionals

Some very familiar codevar data: [String]?

// ...

let result = data?.first?.uppercased()

Some very familiar codevar data: [String]?

// ...

let result = data?.first?.uppercased()

The operator ?. allows us to declare a workflow that will be executed in order, and will prematurely stop if a nil value is encountered

Let’s try to write the code for ?.extension Optional { func ?.<U>(lhs: Wrapped?, rhs: (Wrapped) -> U) -> U? { switch self { case .some(let value): return .some(rhs(value)) case .none: return nil } } }

Disclaimer : this is a simplified case for methods with no

arguments

Arrays

Let’s manipulate an Arraylet data: [Int] = [0, 1, 2]

let result = data.map { $0 * 2 }.map { $0 * $0 }

Let’s manipulate an Arraylet data: [Int] = [0, 1, 2]

let result = data.map { $0 * 2 }.map { $0 * $0 }

Same thing here: the function map allows us to declare a workflow

A possible implementation for mapextension Array { func map<U>(_ transform: (Element) -> U) -> [U] { var result: [U] = [] for e in self { result.append(transform(e)) } return result } }

Functions

Let’s compose functionslet double: (Int) -> Int = { x in x * 2 } let square: (Int) -> Int = { x in x * x }

infix operator • : AdditionPrecedence

func •<T, U, V>(lhs: (U) -> V, rhs: (T) -> U) -> ((T) -> V) { return { t in lhs(rhs(t)) } }

let result = (double • square)(4) // result == 32

Disclaimer : @escaping attributes have been omitted

Let’s compose functionslet double: (Int) -> Int = { x in x * 2 } let square: (Int) -> Int = { x in x * x }

infix operator • : AdditionPrecedence

func •<T, U, V>(lhs: (U) -> V, rhs: (T) -> U) -> ((T) -> V) { return { t in lhs(rhs(t)) } }

let result = (double • square)(4) // result == 32

We have three similar behaviors, yet backed by very different

implementation

What are the common parts?

• They contain value(s) inside a context

• They add new features to existing types

• They provide an interface to transform/map the inner value

Monad: intuitive definition• Wraps a type inside a context

• Provides a mechanism to create a workflow of transforms

Minimal Monadstruct Monad<T> { let value: T // additional data static func just(_ value: T) -> Monad<T> { return self.init(value: value) } }

infix operator >>> : AdditionPrecedence

func >>> <U, V>(lhs: Monad<U>, rhs: (U) -> Monad<V>) -> Monad<V> { // specific combination code }

Some applications

Writer Monad• We have an application that does a lot of numerical calculation

• It’s hard to keep track of how values have been computed

• We would like to have a way to store a value along with a log of all the transformation it went through

Writer Monadstruct Logged<T> { let value: T let logs: [String] private init(value: T) { self.value = value self.logs = ["initialized with value: \(self.value)"] } static func just(_ value: T) -> Logged<T> { return Logged(value: value) } }

func >>> <U, V>(lhs: Logged<U>, rhs: (U) -> Logged<V>) -> Logged<V> { let computation = rhs(lhs.value) return Logged<V>(value: computation.value, logs: lhs.logs + computation.logs) }

func square(_ value: Int) -> Logged<Int> { let result = value * value return Logged(value: result, log: "\(value) was squared, result: \(result)") }

func halve(_ value: Int) -> Logged<Int> { let result = value / 2 return Logged(value: result, log: "\(value) was halved, result: \(result)") }

Writer Monadlet computation = .just(4) >>> square >>> halve

print(computation)

// Logged<Int>(value: 8, logs: ["initialized with value: 4", "4 was squared, result: 16", "16 was halved, result: 8"])

Reader Monad• We have a function that require environment variables

• We don’t want to hard code those variables, because it makes testing impossible

• We would like to have a way to declare the operation we want to perform, but they would be actually executed only when we provide the environment variables

Reader Monadstruct Reader<E, A> { let g: (E) -> A init(g: @escaping (E) -> A) { self.g = g } func apply(_ e: E) -> A { return g(e) } func flatMap<B>(_ f: @escaping (A) -> Reader<E, B>) -> Reader<E, B> { return Reader<E, B> { e in f(self.g(e)).g(e) } } }

func >>> <E, A, B>(a: Reader<E, A>, f: @autoclosure @escaping (A) -> Reader<E, B>) -> Reader<E, B> { return a.flatMap(f) }

Reader Monadstruct Environment { var dbPath: String }

func delete(_ userName: String) -> Reader<Environment, Void> { return Reader<Environment, Void> { env in print("Delete \(userName) at DB path: \(env.dbPath)") } }

let testWorkflow = delete("Thor") >>> delete("Loki") let productionWorkflow = delete("Odin")

testWorkflow.apply(Environment(dbPath: "path_to_test")) productionWorkflow.apply(Environment(dbPath: "path_to_prod"))

IO Monad• We like pure functional programs because they have no side-effects

• Unfortunately, programs also need to do I/O, which bears side-effects by definition

• The IO Monad allows us to encapsulate those side effects, and use them in a pure functional way

IO Monadenum IO<T> { case value(T) case error static func just(_ value: T) -> IO<T> { return .value(value) } }

func wrightLine<T>(_ value: T) -> IO<Void> { print(value as Any) return IO<Void>.just(()) }

func getLine(_: Void) -> IO<String> { switch readLine() { case let value?: return .value(value) case nil: return .error } }

@discardableResult func >>> <U, V>(lhs: IO<U>, rhs: (U) -> IO<V>) -> IO<V> { switch lhs { case .error: return .error case .value(let lhs): return rhs(lhs) } }

IO Monad.just(()) >>> getLine >>> { IO<String>.just($0.uppercased()) } >>> wrightLine

> Hello world!

// HELLO WORLD!

Formal definitionstruct Monad<T> { let value: T static func just(_ value: T) -> Monad<T> } func >>> <U, V>(lhs: Monad<U>, rhs: (U) -> Monad<V>) -> Monad<V>

The following API:

Is a Monad if

let x: T, (.just(x) >>> f) == f(x)let m: (U) -> Monad(V), (m >>> just) == mlet f, g, h: (T) -> T, f >>> g >>> h == f >>> { x in f(x) >>> h(x)

AssociativityNeutral element

How about mobile apps?

Questions ?

Bibliography• https://en.wikipedia.org/wiki/Monad_(functional_programming)

• https://www.youtube.com/watch?v=ZhuHCtR3xq8 (Brian Beckman: Don't fear the Monad)

• https://academy.realm.io/posts/slug-raheel-ahmad-using-monads-functional-paradigms-in-practice-functors-patterns-swift/ (Using Monads and Other Functional Paradigms in Practice)

• https://github.com/orakaro/Swift-monad-Maybe-Reader-and-Try

top related