diff --git a/0035-advanced-protocol-witnesses-pt1/README.md b/0035-advanced-protocol-witnesses-pt1/README.md new file mode 100644 index 00000000..59c88eed --- /dev/null +++ b/0035-advanced-protocol-witnesses-pt1/README.md @@ -0,0 +1,5 @@ +## [Point-Free](https://www.pointfree.co) + +> #### This directory contains code from Point-Free Episode: [Advanced Protocol Witnesses: Part 1](https://www.pointfree.co/episodes/ep35-advanced-protocol-witnesses-part-1) +> +> Now that we know it’s possible to replace protocols with concrete datatypes, and now that we’ve seen how that opens up new ways to compose things that were previously hidden from us, let’s go a little deeper. We will show how to improve the ergonomics of writing Swift in this way, and show what Swift’s powerful conditional conformance feature is represented by just plain functions. diff --git a/0035-advanced-protocol-witnesses-pt1/Witness.xcworkspace/contents.xcworkspacedata b/0035-advanced-protocol-witnesses-pt1/Witness.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..d0003ede --- /dev/null +++ b/0035-advanced-protocol-witnesses-pt1/Witness.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/0035-advanced-protocol-witnesses-pt1/Witness.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/0035-advanced-protocol-witnesses-pt1/Witness.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/0035-advanced-protocol-witnesses-pt1/Witness.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/Contents.swift b/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/Contents.swift new file mode 100644 index 00000000..44420241 --- /dev/null +++ b/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/Contents.swift @@ -0,0 +1,247 @@ +import Darwin + +struct Predicate { + let contains: (A) -> Bool + func contramap(_ f: @escaping (B) -> A) -> Predicate { + return Predicate { self.contains(f($0)) } + } + func pullback(_ f: @escaping (B) -> A) -> Predicate { + return Predicate { self.contains(f($0)) } + } +} + +let isLessThan10 = Predicate { $0 < 10 } +isLessThan10.contains(5) +isLessThan10.contains(11) + +//let shortStrings = isLessThan10.contramap { (s: String) in s.count } + +import Overture + +let shortStrings = isLessThan10.contramap(get(\String.count)) + +shortStrings.contains("Blob") +shortStrings.contains("Blobby McBlob") + +isLessThan10.pullback(get(\String.count)) + + +struct Describing { + let describe: (A) -> String + + func contramap(_ f: @escaping (B) -> A) -> Describing { + return Describing { b in + self.describe(f(b)) + } + } + + func pullback(_ f: @escaping (B) -> A) -> Describing { + return Describing { b in + self.describe(f(b)) + } + } +} + +struct PostgresConnInfo { + var database: String + var hostname: String + var password: String + var port: Int + var user: String +} + + +let compactWitness = Describing { conn in + return "PostgresConnInfo(database: \"\(conn.database)\", hostname: \"\(conn.hostname)\", password: \"\(conn.password)\", port: \"\(conn.port)\", user: \"\(conn.user)\")" +} + +let prettyWitness = Describing { + """ + PostgresConnInfo( + database: \"\($0.database)\", + hostname: \"\($0.hostname)\", + password: \"\($0.password)\", + port: \"\($0.port)\", + user: \"\($0.user)\" + ) + """ +} + +let secureCompactWitness = compactWitness.contramap(set(\.password, "*******")) + +let securePrettyWitness = prettyWitness.contramap(set(\.password, "******")) + +compactWitness.pullback(set(\.password, "******")) + + +protocol Combinable { + func combine(with other: Self) -> Self +} + +struct Combining { + let combine: (A, A) -> A +} +struct EmptyInitializing { + let create: () -> A +} + +let sum = Combining(combine: +) +let zero = EmptyInitializing { 0 } + +let product = Combining(combine: *) +let one = EmptyInitializing { 1 } + +extension Array { + func reduce(_ initial: EmptyInitializing, _ combining: Combining) -> Element { + return self.reduce(initial.create(), combining.combine) + } +} + +[1, 2, 3, 4].reduce(zero, sum) +[1, 2, 3, 4].reduce(one, product) + +//extension Combining where A == Int { +// static let sum = Combining(combine: +) +// static let product = Combining(combine: *) +//} +// +//extension EmptyInitializing where A == Int { +// static let zero = EmptyInitializing { 0 } +// static let one = EmptyInitializing { 1 } +//} + +extension Combining where A: Numeric { + static var sum: Combining { + return Combining(combine: +) + } + static var product: Combining { + return Combining(combine: *) + } +} + +extension EmptyInitializing where A: Numeric { + static var zero: EmptyInitializing { + return EmptyInitializing { 0 } + } + static var one: EmptyInitializing { + return EmptyInitializing { 1 } + } +} + + +[1, 2, 3, 4].reduce(EmptyInitializing.zero, Combining.sum) +[1, 2, 3, 4].reduce(EmptyInitializing.one, Combining.product) + +[1, 2, 3, 4].reduce(.zero, .sum) +[1, 2, 3, 4].reduce(.one, .product) + +[1.1, 2, 3, 4].reduce(.zero, .sum) +[1.1, 2, 3, 4].reduce(.one, .product) + +extension Describing where A == PostgresConnInfo { + + static let compact = Describing { conn in + return "PostgresConnInfo(database: \"\(conn.database)\", hostname: \"\(conn.hostname)\", password: \"\(conn.password)\", port: \"\(conn.port)\", user: \"\(conn.user)\")" + } + + static let pretty = Describing { + """ + PostgresConnInfo( + database: \"\($0.database)\", + hostname: \"\($0.hostname)\", + password: \"\($0.password)\", + port: \"\($0.port)\", + user: \"\($0.user)\" + ) + """ + } + +} + +let localhostPostgres = PostgresConnInfo( + database: "pointfreeco_development", + hostname: "localhost", + password: "", + port: 5432, + user: "pointfreeco" +) + +func print(tag: String, _ value: A, _ witness: Describing) { + print("[\(tag)] \(witness.describe(value))") +} + +print(tag: "debug", localhostPostgres, compactWitness) +print(tag: "debug", localhostPostgres, .compact) + +extension Describing where A == Bool { + static let compact = Describing { $0 ? "t" : "f" } + static let pretty = Describing { $0 ? "𝓣𝓻𝓾𝓮" : "𝓕𝓪𝓵𝓼𝓮" } +} + +print(tag: "debug", true, .compact) +print(tag: "debug", true, .pretty) + +extension Array: Equatable where Element: Equatable { + //... +} + + +// public protocol Equatable { +// public static func == (lhs: Self, rhs: Self) -> Bool +// } + +struct Equating { + let equals: (A, A) -> Bool + + func pullback(_ f: @escaping (B) -> A) -> Equating { + return Equating { lhs, rhs in + self.equals(f(lhs), f(rhs)) + } + } +} + +extension Equating where A == Int { + static let int = Equating(equals: ==) +} + +extension Equating { + static func array(of equating: Equating) -> Equating<[A]> { + return Equating<[A]> { lhs, rhs in + guard lhs.count == rhs.count else { return false } + + for (lhs, rhs) in zip(lhs, rhs) { + if !equating.equals(lhs, rhs) { + return false + } + } + + return true + } + } +} + +Equating.array(of: .int).equals([], []) +Equating.array(of: .int).equals([1], [1]) +Equating.array(of: .int).equals([1], [1, 2]) + +let stringCount = Equating.int.pullback(get(\String.count)) + +Equating.array(of: stringCount).equals([], []) +Equating.array(of: stringCount).equals(["Blob"], ["Blob"]) +Equating.array(of: stringCount).equals(["Blob"], ["Bolb"]) +Equating.array(of: stringCount).equals(["Blob"], ["Blob Sr"]) + + +//[[Int]] + +[[1, 2], [3, 4]] == [[1, 2], [3, 4, 5]] +[[1, 2], [3, 4]] == [[1, 2], [3, 4]] + +(Equating.array >>> Equating.array)(.int).equals([[1, 2], [3, 4]] , [[1, 2], [3, 4]]) +(Equating.array >>> Equating.array)(.int).equals([[1, 2], [3, 4]] , [[1, 2], [3, 4, 5]]) + +(Equating.array >>> Equating.array)(stringCount) + +(Equating.array >>> Equating.array)(stringCount).equals([["Blob"], ["Blob Jr"]], [["Bolb"], ["Bolb Jr"]]) + +(Equating.array >>> Equating.array)(stringCount).equals([["Blob"], ["Blob Jr"]], [["Bolb"], ["Bolb Esq"]]) diff --git a/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/Sources/Util.swift b/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/Sources/Util.swift new file mode 100644 index 00000000..b402c2a6 --- /dev/null +++ b/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/Sources/Util.swift @@ -0,0 +1,18 @@ + +infix operator >>> + +public func >>> (f: @escaping (A) -> B, g: @escaping (B) -> C) -> (A) -> C { + return { g(f($0)) } +} + +open class UITableViewCell {} + +infix operator |> +public func |> (_ a: A, f: (A) -> B) -> B { + return f(a) +} + +infix operator <| +public func <| (f: (A) -> B, _ a: A) -> B { + return f(a) +} diff --git a/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/contents.xcplayground b/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/contents.xcplayground new file mode 100644 index 00000000..63b6dd8d --- /dev/null +++ b/0035-advanced-protocol-witnesses-pt1/Witnesses.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index 476d61d1..dbae7907 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,4 @@ This repository is the home of code written on episodes of 1. [Decodable Randomness: Part 2](0032-arbitrary-pt2) 1. [Protocol Witnesses: Part 1](0033-protocol-witnesses-pt1) 1. [Protocol Witnesses: Part 2](0034-protocol-witnesses-pt2) +1. [Advanced Protocol Witnesses: Part 1](0035-advanced-protocol-witnesses-pt1)