Skip to content

Instantly share code, notes, and snippets.

@nikitamounier
Created October 25, 2021 10:18
Show Gist options
  • Save nikitamounier/24f01e6ae2b50d0974adab712db69503 to your computer and use it in GitHub Desktop.
Save nikitamounier/24f01e6ae2b50d0974adab712db69503 to your computer and use it in GitHub Desktop.
A simple PredicateSet type, with only one property of type (Element) -> Bool, which (nearly) conforms to SetAlgebra.
struct PredicateSet<Element: Equatable> {
var contains: (Element) -> Bool
init(_ contains: @escaping (Element) -> Bool) {
self.contains = contains
}
}
extension PredicateSet: SetAlgebra {
init() {
self.contains = { _ in false }
}
func contains(_ member: Element) -> Bool {
return self.contains(member)
}
func union(_ other: PredicateSet<Element>) -> PredicateSet<Element> {
return Self {
self.contains($0) || other.contains($0)
}
}
func intersection(_ other: PredicateSet<Element>) -> PredicateSet<Element> {
return Self {
self.contains($0) && other.contains($0)
}
}
func symmetricDifference(_ other: PredicateSet<Element>) -> PredicateSet<Element> {
return Self {
(self.contains($0) && !other.contains($0)) || (!self.contains($0) && other.contains($0))
}
}
mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
if self.contains(newMember) {
return (false, newMember)
}
let contains = self.contains
self.contains = {
return contains($0) || $0 == newMember
}
return (true, newMember)
}
mutating func remove(_ member: Element) -> Element? {
if self.contains(member) {
let contains = self.contains
self.contains = {
return contains($0) && $0 != member
}
return member
}
return nil
}
mutating func update(with newMember: Element) -> Element? {
if self.contains(newMember) {
let contains = self.contains
self.contains = {
return contains($0) || $0 == newMember
}
return newMember
}
return nil
}
mutating func formUnion(_ other: PredicateSet<Element>) {
let contains = self.contains
self.contains = {
contains($0) || other.contains($0)
}
}
mutating func formIntersection(_ other: PredicateSet<Element>) {
let contains = self.contains
self.contains = {
contains($0) && other.contains($0)
}
}
mutating func formSymmetricDifference(_ other: PredicateSet<Element>) {
let contains = self.contains
self.contains = {
(contains($0) && !other.contains($0)) || (!contains($0) && other.contains($0))
}
}
static func == (lhs: PredicateSet<Element>, rhs: PredicateSet<Element>) -> Bool {
fatalError("Cannot test equality between two PredicateSets.")
}
}
extension PredicateSet {
func pullback<ParentElement>(_ toElement: @escaping (ParentElement) -> Element) -> PredicateSet<ParentElement> {
return PredicateSet<ParentElement> {
self.contains(toElement($0))
}
}
}
// 2, 4, 6, 8, 10, 12...
var predicate: PredicateSet<Int> = PredicateSet { $0.isMultiple(of: 2) }
predicate.contains(2) // true
predicate.contains(45) // false
// 101, 102, 103, 104, 105, 106, 107, 108...
let biggerThan100: PredicateSet<Int> = PredicateSet { $0 > 100 }
// 102, 104, 106, 108, 110, 112...
predicate.formIntersection(biggerThan100)
predicate.contains(98) // false
predicate.contains(102) // true
// empty
predicate.formSymmetricDifference(PredicateSet { $0.isMultiple(of: 2) })
predicate.contains(128) // false
// -2^63 -> (2^63)-1
predicate.formUnion(PredicateSet { _ in true })
predicate.contains(-109238) // true
predicate.remove(0)
predicate.contains(0) // false
// ... -4, -2, -1, 1, 2, 4, 5, 7, 8, 10 ...
predicate.subtract(PredicateSet { $0.isMultiple(of: 3) })
predicate.contains(300) // false
predicate.insert(3)
predicate.contains(3) // true
struct Person: Equatable {
let id: Int
let name: String
}
let personPredicate: PredicateSet<Person> = predicate.pullback(\.id)
let paul = Person(id: 367, name: "Paul")
let chani = Person(id: -333, name: "Chani")
personPredicate.contains(paul) // true
personPredicate.contains(chani) // false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment