diff --git a/0044-the-many-faces-of-flatmap-pt3/ManyFacesOfFlatMap.playground/Contents.swift b/0044-the-many-faces-of-flatmap-pt3/ManyFacesOfFlatMap.playground/Contents.swift index b63079f2..47331164 100644 --- a/0044-the-many-faces-of-flatmap-pt3/ManyFacesOfFlatMap.playground/Contents.swift +++ b/0044-the-many-faces-of-flatmap-pt3/ManyFacesOfFlatMap.playground/Contents.swift @@ -119,15 +119,15 @@ struct User: Codable { let name: String } -//let user: User? -//if let path = Bundle.main.path(forResource: "user", ofType: "json"), -// case let url = URL.init(fileURLWithPath: path), -// let data = try? Data.init(contentsOf: url) { -// -// user = try? JSONDecoder().decode(User.self, from: data) -//} else { -// user = nil -//} +let user: User? +if let path = Bundle.main.path(forResource: "user", ofType: "json"), + case let url = URL.init(fileURLWithPath: path), + let data = try? Data.init(contentsOf: url) { + + user = try? JSONDecoder().decode(User.self, from: data) +} else { + user = nil +} let newUser = Bundle.main.path(forResource: "user", ofType: "json") .map(URL.init(fileURLWithPath:)) @@ -148,7 +148,7 @@ let invoices = Bundle.main.path(forResource: "invoices", ofType: "json") .flatMap { try? JSONDecoder().decode([Invoice].self, from: $0) } if let newUser = newUser, let invoices = invoices { - // + } func zip(_ a: A?, _ b: B?) -> (A, B)? { @@ -183,7 +183,6 @@ do { } - extension Result where E == Swift.Error { init(catching f: () throws -> A) { do { @@ -204,8 +203,6 @@ extension Validated where E == Swift.Error { } } - - func zip(_ a: Result, _ b: Result) -> Result<(A, B), E> { switch (a, b) { case let (.success(a), .success(b)): @@ -217,12 +214,10 @@ func zip(_ a: Result, _ b: Result) -> Result<(A, B), E> { } } - func zip(with f: @escaping (A, B) -> C) -> (Result, Result) -> Result { return { zip($0, $1).map(f) } } - func zip(_ a: Validated, _ b: Validated) -> Validated<(A, B), E> { switch (a, b) { case let (.valid(a), .valid(b)): @@ -354,329 +349,23 @@ zip(with: UserEnvelope.init)( .flatMap { url in Parallel(try! Data.init(contentsOf: url)) } .flatMap { data in Parallel(try! JSONDecoder().decode([Invoice].self, from: data)) } ).run { env in -// print(env) -} - -// -//do { -// let user = try JSONDecoder().decode( -// User.self, -// from: Data.init( -// contentsOf: URL.init( -// fileURLWithPath: requireSome( -// Bundle.main.path( -// forResource: "user", -// ofType: "json" -// ) -// ) -// ) -// ) -// ) -//} catch { -// -//} - - -//extension Sequence { -// public func flatMap(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult] -//} - -// flatMap: ((A) -> B?) -> ((F) -> F) - - -//func fromThrowing(_ f: @escaping (A) throws -> B) -> (A) -> Result { -// return { a in -// do { -// return .success(try f(a)) -// } catch let error { -// return .failure(error) -// } -// } -//} -// -//func toThrowing(_ f: @escaping (A) -> Result) -> ((A) throws -> B) { -// return { a in -// switch f(a) { -// case let .success(value): -// return value -// case let .failure(error): -// throw error -// } -// } -//} -// -//extension Result { -// func map(_ f: @escaping (A) -> Result) -> Result { -// fatalError() -// } -// -// func flatMap(_ f: @escaping (A) -> Result, Swift.Error>) -> Result { -// fatalError() -// } -//} - -//extension Func /* */ { -// func flatMap(_ f: @escaping (A) -> Func) -> Func { -// -// } -//} - -import XCTest -struct Diffing { - let toData: (Value) -> Data - let fromData: (Data) -> Value - let diff: (Value, Value) -> (String, [XCTAttachment])? - - func flatMap(_ f: @escaping (Value) -> Diffing) -> Diffing { - fatalError("Not possible to implement.") - } -} - -struct Snapshotting { - var pathExtension: String? - let diffing: Diffing - let snapshot: (Value) -> Format - - func flatMap(_ f: @escaping (Value) -> Snapshotting) -> Snapshotting { - fatalError("Not possible to implement.") - } - - func flatMap(_ f: @escaping (Format) -> Snapshotting) -> Snapshotting { - fatalError("Not possible to implement.") - } + print(env) } - -extension Parallel { - func then(_ f: @escaping (A) -> Parallel) -> Parallel { - return self.flatMap(f) - } -} - -extension Parallel { - func then(_ f: @escaping (A) -> B) -> Parallel { - return self.map(f) - } -} - -Parallel(Bundle.main.path(forResource: "user", ofType: "json")!) - .then(URL.init(fileURLWithPath:)) - .then { url in Parallel(try! Data.init(contentsOf: url)) } - .then { data in Parallel(try! JSONDecoder().decode(User.self, from: data)) } - -Parallel(Bundle.main.path(forResource: "user", ofType: "json")!) - .map(URL.init(fileURLWithPath:)) - .flatMap { url in Parallel(try! Data.init(contentsOf: url)) } - .flatMap { data in Parallel(try! JSONDecoder().decode(User.self, from: data)) } - - - - -func pipe( - _ lhs: @escaping (A) -> B, - _ rhs: @escaping (B) -> C - ) -> (A) -> C { - return lhs >>> rhs -} - -pipe({ $0 + 1 }, { $0 * $0 }) -pipe({ $0 + 1 }, String.init) - -let f = { $0 + 1 } - >>> { $0 * $0 } - >>> { $0 + 1 } - - -//_ = { try? Data.init(contentsOf: $0) } -// >>> { try? JSONDecoder().decode(User.self, from: $0) } - -func chain( - _ lhs: @escaping (A) -> B?, - _ rhs: @escaping (B) -> C? - ) -> (A) -> C? { - return { a in - lhs(a).flatMap(rhs) - } -} - -func >=> ( - _ lhs: @escaping (A) -> B?, - _ rhs: @escaping (B) -> C? - ) -> (A) -> C? { - return { a in - lhs(a).flatMap(rhs) - } -} - - -pipe( - URL.init(fileURLWithPath:), - chain( - { try? Data.init(contentsOf: $0) }, - { try? JSONDecoder().decode(User.self, from: $0) } +do { + let user = try JSONDecoder().decode( + User.self, + from: Data.init( + contentsOf: URL.init( + fileURLWithPath: requireSome( + Bundle.main.path( + forResource: "user", + ofType: "json" + ) + ) + ) + ) ) -) - -let loadUser = URL.init(fileURLWithPath:) - >>> { try? Data.init(contentsOf: $0) } - >=> { try? JSONDecoder().decode(User.self, from: $0) } - -Bundle.main.path(forResource: "user", ofType: "json") - .map(URL.init(fileURLWithPath:)) - .flatMap { try? Data.init(contentsOf: $0) } - .flatMap { try? JSONDecoder().decode(User.self, from: $0) } - -Bundle.main.path(forResource: "user", ofType: "json") - .flatMap(loadUser) - - -// Parallel> - -func map( - _ f: @escaping (A) -> B - ) -> (Parallel>) -> Parallel> { - - return { parallelResultA in - parallelResultA.map { resultA in - resultA.map { a in - f(a) - } - } - } -} - -func zip( - _ lhs: Parallel>, - _ rhs: Parallel> - ) -> Parallel> { - - return zip(with: zip)(lhs, rhs) - -// return zip(lhs, rhs).map { resultA, resultB in -// zip(resultA, resultB) -// } -} - -func flatMap( - _ f: @escaping (A) -> Parallel> - ) -> (Parallel>) -> Parallel> { - - return { parallelResultA in - parallelResultA.flatMap { resultA in - Parallel> { callback in - switch resultA { - case let .success(a): - f(a).run { resultB in callback(resultB) } - case let .failure(error): - callback(.failure(error)) - } - } - } - } -} - - -extension Optional { - func newMap(_ f: (Wrapped) -> NewWrapped) -> NewWrapped? { - return self.flatMap { Optional.some(f($0)) } - } -} - -extension Array { - func newMap(_ f: (Element) -> NewElement) -> [NewElement] { - return self.flatMap { [f($0)] } - } -} - -extension Result { - func newMap(_ f: (A) -> B) -> Result { - return self.flatMap { .success(f($0)) } - } -} - -extension Validated { - func newMap(_ f: (A) -> B) -> Validated { - return self.flatMap { .valid(f($0)) } - } -} - -extension Func { - func newMap(_ f: @escaping (B) -> C) -> Func { - return self.flatMap { b in Func { _ in f(b) } } - } -} - -extension Parallel { - func newMap(_ f: @escaping (A) -> B) -> Parallel { - return self.flatMap { a in Parallel { callback in callback(f(a)) } } - } -} - -func newZip(_ a: A?, _ b: B?) -> (A, B)? { - return a.flatMap { a in - b.flatMap { b in - Optional.some((a, b)) - } - } -} - -func newZip(_ a: [A], _ b: [B]) -> [(A, B)] { - return a.flatMap { a in - b.flatMap { b in - [(a, b)] - } - } -} - -newZip(["a", "b"], [1, 2]) - -func newZip(_ a: Result, _ b: Result) -> Result<(A, B), E> { - return a.flatMap { a in - b.flatMap { b in - Result.success((a, b)) - } - } -} - -func newZip(_ a: Validated, _ b: Validated) -> Validated<(A, B), E> { - return a.flatMap { a in - b.flatMap { b in - Validated.valid((a, b)) - } - } -} - -newZip(Validated.valid(1), .valid("Two")) - - -newZip(Validated.invalid(NonEmptyArray("Something went wrong.")), .valid(2)) - -newZip( - Validated.invalid(NonEmptyArray("Something went wrong.")), - Validated.invalid(NonEmptyArray("Something else went wrong.")) -) - -func newZip(_ a: Func, _ b: Func) -> Func { - return a.flatMap { a in - b.flatMap { b in - Func { _ in (a, b) } - } - } -} - -func newZip(_ a: Parallel, _ b: Parallel) -> Parallel<(A, B)> { - return a.flatMap { a in - b.flatMap { b in - Parallel { callback in callback((a, b)) } - } - } -} +} catch { -newZip(delay(by: 2).map { 2 }, delay(by: 3).map { 3 }).run { - print($0) } - - - - - diff --git a/0044-the-many-faces-of-flatmap-pt3/README.md b/0044-the-many-faces-of-flatmap-pt3/README.md index 0abebd97..bb448a81 100644 --- a/0044-the-many-faces-of-flatmap-pt3/README.md +++ b/0044-the-many-faces-of-flatmap-pt3/README.md @@ -2,13 +2,13 @@ > #### This directory contains code from Point-Free Episode: [The Many Faces of Flat-Map: Part 2](https://www.pointfree.co/episodes/ep43-the-many-faces-of-flat-map-part-2) > -> Now that we know that `flatMap` is important for flattening nested arrays and optionals, we should feel empowered to define it on our own types. This leads us to understanding its structure more in depth and how it’s different from `map` and `zip`. +> We are now ready to answer the all-important question: what's the point? We will describe 3 important ideas that are now more accessible due to our deep study of `map`, `zip` and `flatMap`. We will start by showing that this trio of operations forms a kind of functional, domain-specific language for data transformations. ### Getting Started * Clone repo * `cd` into this directory * Run `swift package generate-xcodeproj` -* Open `ManyFacesOfFlatMapPt2.xcworkspace` +* Open `ManyFacesOfFlatMapPt.xcworkspace` * Build the package for _macOS_ * Open the playground diff --git a/README.md b/README.md index ee1a90b9..e09324c8 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,4 @@ This repository is the home of code written on episodes of 1. A Tour of Snapshot Testing 1. [The Many Faces of Flat-Map: Part 1](0042-the-many-faces-of-flatmap-pt1) 1. [The Many Faces of Flat-Map: Part 2](0043-the-many-faces-of-flatmap-pt2) +1. [The Many Faces of Flat-Map: Part 3](0044-the-many-faces-of-flatmap-pt3)