-
Notifications
You must be signed in to change notification settings - Fork 299
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
25d5ec7
commit fff1067
Showing
5 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
178 changes: 178 additions & 0 deletions
178
...le-of-two-flat-maps/FilterMap.playground/Pages/01-Episode.xcplaygroundpage/Contents.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
|
||
// extension Array { | ||
// func flatMap<B>(_ 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<B>(_ 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<B>) -> ( Array<A>) -> Array<B> | ||
// flatMap : ((A) -> Optional<B>) -> (Optional<A>) -> Optional<B> | ||
|
||
// flatMap : ((A) -> Optional<B>) -> ( Array<A>) -> Array<B> | ||
|
||
|
||
|
||
// flatMap : ((A) -> M<B>) -> (M<A>) -> M<B> | ||
// flatMap : ((A) -> M<B>) -> (M<A>) -> M<B> | ||
|
||
// flatMap : ((A) -> B?) -> (M<A>) -> M<B> | ||
|
||
|
||
|
||
[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<B>(_ 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<B>(_ 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<A>(_ p: @escaping (A) -> Bool) -> (A) -> A? { | ||
return { p($0) ? .some($0) : .none } | ||
} | ||
|
||
|
||
func filter<A>(_ p: @escaping (A) -> Bool) -> ([A]) -> [A] { | ||
return { $0.filterMap(filterSome(p)) } | ||
} | ||
|
||
|
||
Array(0..<10) | ||
|> filter { $0 % 2 == 0 } | ||
|
||
enum Either<A, B> { | ||
case left(A) | ||
case right(B) | ||
} | ||
|
||
|
||
|
||
func partitionEither<A>(_ p: @escaping (A) -> Bool) -> (A) -> Either<A, A> { | ||
return { p($0) ? .right($0) : .left($0) } | ||
} | ||
|
||
|
||
|
||
|
||
|
||
extension Array { | ||
func partitionMap<A, B>(_ transform: (Element) -> Either<A, B>) -> (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<A>(_ p: @escaping (A) -> Bool) -> ([A]) -> (`false`: [A], `true`: [A]) { | ||
return { | ||
let (lefts, rights) = $0.partitionMap(partitionEither(p)) | ||
return (lefts, rights) | ||
} | ||
} | ||
|
||
|
||
|
||
func partitionMap<A, B, C>(_ p: @escaping (A) -> Either<B, C>) -> ([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! |
29 changes: 29 additions & 0 deletions
29
...-of-two-flat-maps/FilterMap.playground/Pages/02-Exercises.xcplaygroundpage/Contents.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<A, B>]` 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`? | ||
*/ |
80 changes: 80 additions & 0 deletions
80
0010-a-tale-of-two-flat-maps/FilterMap.playground/Sources/Util.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |> <A, B>(x: A, f: (A) -> B) -> B { | ||
return f(x) | ||
} | ||
|
||
public func >>> <A, B, C>(f: @escaping (A) -> B, g: @escaping (B) -> C) -> (A) -> C { | ||
return { g(f($0)) } | ||
} | ||
|
||
public func zurry<A>(_ f: () -> A) -> A { | ||
return f() | ||
} | ||
|
||
public func flip<A, B, C>(_ f: @escaping (A) -> (B) -> C) -> (B) -> (A) -> C { | ||
|
||
return { b in { a in f(a)(b) } } | ||
} | ||
|
||
public func flip<A, C>(_ f: @escaping (A) -> () -> C) -> () -> (A) -> C { | ||
|
||
return { { a in f(a)() } } | ||
} | ||
|
||
|
||
precedencegroup SingleTypeComposition { | ||
associativity: left | ||
higherThan: ForwardApplication | ||
} | ||
|
||
infix operator <>: SingleTypeComposition | ||
|
||
public func <> <A>(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 <<< <A, B, C>(_ f: @escaping (B) -> C, _ g: @escaping (A) -> B) -> (A) -> C { | ||
return { f(g($0)) } | ||
} | ||
|
||
public func second<A, B, C>(_ f: @escaping (B) -> C) -> ((A, B)) -> (A, C) { | ||
return { pair in | ||
return (pair.0, f(pair.1)) | ||
} | ||
} | ||
|
||
public func map<A, B>(_ f: @escaping (A) -> B) -> ([A]) -> [B] { | ||
return { $0.map(f) } | ||
} | ||
|
||
public func first<A, B, C>(_ f: @escaping (A) -> B) -> ((A, C)) -> (B, C) { | ||
return { pair in | ||
(f(pair.0), pair.1) | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
0010-a-tale-of-two-flat-maps/FilterMap.playground/contents.xcplayground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
<playground version='6.0' target-platform='macos' display-mode='rendered' executeOnSourceChanges='false'> | ||
<pages> | ||
<page name='01-Episode'/> | ||
<page name='02-Exercises'/> | ||
</pages> | ||
</playground> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |