diff --git a/0010-a-tale-of-two-flat-maps/FilterMap.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift b/0010-a-tale-of-two-flat-maps/FilterMap.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..1223aa98 --- /dev/null +++ b/0010-a-tale-of-two-flat-maps/FilterMap.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift @@ -0,0 +1,178 @@ + +// extension Array { +// func flatMap(_ f: @escaping (Element) -> [B]) -> [B] { +// } +// } + +let csv = """ +1,2,3,4 +3,5,2 +8,9,4 +""" + +csv + .split(separator: "\n") + .map { $0.split(separator: ",") } + + + +// extension Optional { +// func flatMap(_ f: @escaping (Element) -> B?) -> B? { +// } +// } + +String.init(data: Data(), encoding: .utf8) + .map(Int.init) + +let _: Int? = String.init(data: Data([55]), encoding: .utf8) + .flatMap(Int.init) + +["1", "2", "buckle", "my", "shoe"] + .map(Int.init) + +["1", "2", "buckle", "my", "shoe"] + .flatMap(Int.init) + +csv.split(separator: "\n") + .flatMap { $0.split(separator: ",") } + .flatMap { Int($0) } + .reduce(0, +) + + + + +// flatMap : ((A) -> [B]) -> ([A]) -> [B] +// flatMap : ((A) -> B?) -> ( A?) -> B? + +// flatMap : ((A) -> B?) -> ([A]) -> [B] + + + +// flatMap : ((A) -> Array) -> ( Array) -> Array +// flatMap : ((A) -> Optional) -> (Optional) -> Optional + +// flatMap : ((A) -> Optional) -> ( Array) -> Array + + + +// flatMap : ((A) -> M) -> (M) -> M +// flatMap : ((A) -> M) -> (M) -> M + +// flatMap : ((A) -> B?) -> (M) -> M + + + +[1, 2, 3] + .flatMap { $0 + 1 } + + + +struct User { + let name: String +} + +let users = [User(name: "Blob"), User(name: "Math")] +users + .map { $0.name } +users + .flatMap { $0.name } + + + +extension Array { + func filterMap(_ transform: (Element) -> B?) -> [B] { + var result = [B]() + for x in self { + switch transform(x) { + case let .some(x): + result.append(x) + case .none: + continue + } + } + return result + } +} + + + +extension Array { + func compactMap(_ transform: (Element) -> B?) -> [B] { + var result = [B]() + for x in self { + switch transform(x) { + case let .some(x): + result.append(x) + case .none: + continue + } + } + return result + } +} + + + +func filterSome(_ p: @escaping (A) -> Bool) -> (A) -> A? { + return { p($0) ? .some($0) : .none } +} + + +func filter(_ p: @escaping (A) -> Bool) -> ([A]) -> [A] { + return { $0.filterMap(filterSome(p)) } +} + + +Array(0..<10) + |> filter { $0 % 2 == 0 } + +enum Either { + case left(A) + case right(B) +} + + + +func partitionEither(_ p: @escaping (A) -> Bool) -> (A) -> Either { + return { p($0) ? .right($0) : .left($0) } +} + + + + + +extension Array { + func partitionMap(_ transform: (Element) -> Either) -> (lefts: [A], rights: [B]) { + var result = (lefts: [A](), rights: [B]()) + for x in self { + switch transform(x) { + case let .left(a): + result.lefts.append(a) + case let .right(b): + result.rights.append(b) + } + } + return result + } +} + +func partition(_ p: @escaping (A) -> Bool) -> ([A]) -> (`false`: [A], `true`: [A]) { + return { + let (lefts, rights) = $0.partitionMap(partitionEither(p)) + return (lefts, rights) + } +} + + + +func partitionMap(_ p: @escaping (A) -> Either) -> ([A]) -> (lefts: [B], rights: [C]) { + return { $0.partitionMap(p) } +} + +let evenOdds = { $0 % 2 == 0 ? Either.left($0) : .right($0) } +partitionMap(evenOdds) + +Array(1...10) + |> partitionMap(evenOdds) + |> (first <<< map)(square) +//: [See the next page](@next) for exercises! diff --git a/0010-a-tale-of-two-flat-maps/FilterMap.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift b/0010-a-tale-of-two-flat-maps/FilterMap.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift new file mode 100644 index 00000000..d2f4c45a --- /dev/null +++ b/0010-a-tale-of-two-flat-maps/FilterMap.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift @@ -0,0 +1,29 @@ +/*: + # Algebraic Data Type: Exponents, Exercises + + 1. Define `filtered` as a function from `[A?]` to `[A]`. + */ +// TODO +/*: + 2. Define `partitioned` as a function from `[Either]` to `(left: [A], right: [B])`. What does this function have in common with `filtered`? + */ +// TODO +/*: + 3. Define `partitionMap` on `Optional`. + */ +// TODO +/*: + 4. Dictionary has `mapValues`, which takes a transform function from `(Value) -> B` to produce a new dictionary of type `[Key: B]`. Define `filterMapValues` on `Dictionary`. + // - Define `partitionMapValues` on `Dictionary`. + */ +// TODO +/*: + 5. Define `partitionMapValues` on `Dictionary`. + */ +// TODO +/*: + 6. Rewrite `filterMap` and `filter` in terms of `partitionMap`. + */ +/*: + 7. Is it possible to define `partitionMap` on `Either`? + */ diff --git a/0010-a-tale-of-two-flat-maps/FilterMap.playground/Sources/Util.swift b/0010-a-tale-of-two-flat-maps/FilterMap.playground/Sources/Util.swift new file mode 100644 index 00000000..148728ee --- /dev/null +++ b/0010-a-tale-of-two-flat-maps/FilterMap.playground/Sources/Util.swift @@ -0,0 +1,80 @@ +@_exported import Foundation + +precedencegroup ForwardApplication { + associativity: left +} + +infix operator |>: ForwardApplication + +precedencegroup ForwardComposition { + associativity: left + higherThan: ForwardApplication +} + +infix operator >>>: ForwardComposition + +public func |> (x: A, f: (A) -> B) -> B { + return f(x) +} + +public func >>> (f: @escaping (A) -> B, g: @escaping (B) -> C) -> (A) -> C { + return { g(f($0)) } +} + +public func zurry(_ f: () -> A) -> A { + return f() +} + +public func flip(_ f: @escaping (A) -> (B) -> C) -> (B) -> (A) -> C { + + return { b in { a in f(a)(b) } } +} + +public func flip(_ f: @escaping (A) -> () -> C) -> () -> (A) -> C { + + return { { a in f(a)() } } +} + + +precedencegroup SingleTypeComposition { + associativity: left + higherThan: ForwardApplication +} + +infix operator <>: SingleTypeComposition + +public func <> (f: @escaping (A) -> A, g: @escaping (A) -> A) -> (A) -> A { + return f >>> g +} + +public func incr(_ x: Int) -> Int { + return x + 1 +} + +public func square(_ x: Int) -> Int { + return x * x +} + +precedencegroup BackwardsComposition { + associativity: left +} +infix operator <<<: BackwardsComposition +public func <<< (_ f: @escaping (B) -> C, _ g: @escaping (A) -> B) -> (A) -> C { + return { f(g($0)) } +} + +public func second(_ f: @escaping (B) -> C) -> ((A, B)) -> (A, C) { + return { pair in + return (pair.0, f(pair.1)) + } +} + +public func map(_ f: @escaping (A) -> B) -> ([A]) -> [B] { + return { $0.map(f) } +} + +public func first(_ f: @escaping (A) -> B) -> ((A, C)) -> (B, C) { + return { pair in + (f(pair.0), pair.1) + } +} diff --git a/0010-a-tale-of-two-flat-maps/FilterMap.playground/contents.xcplayground b/0010-a-tale-of-two-flat-maps/FilterMap.playground/contents.xcplayground new file mode 100644 index 00000000..a9efbf36 --- /dev/null +++ b/0010-a-tale-of-two-flat-maps/FilterMap.playground/contents.xcplayground @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/0010-a-tale-of-two-flat-maps/README.md b/0010-a-tale-of-two-flat-maps/README.md new file mode 100644 index 00000000..0f41de4b --- /dev/null +++ b/0010-a-tale-of-two-flat-maps/README.md @@ -0,0 +1,10 @@ +### [Point-Free](https://www.pointfree.co) Episode #10 + +### A Tale of Two Flat-Maps + +> The most recent version of Swift, 4.1, has deprecated and renamed a particular overload of `flatMap`. We +> want to take a moment to understand what made this `flatMap` different from all the others, and explore +> generalizations of the operation to other structures. + +This directory contains code from Point-Free Episode #10: +[A Tale of Two Flat-Maps](https://www.pointfree.co/episodes/ep10-a-tale-of-two-flat-maps)