Skip to content

Instantly share code, notes, and snippets.

@elm4ward
Created December 15, 2017 12:21
Show Gist options
  • Save elm4ward/e28666398794c86decf6c46a7972d171 to your computer and use it in GitHub Desktop.
Save elm4ward/e28666398794c86decf6c46a7972d171 to your computer and use it in GitHub Desktop.
let flag: (Bool) -> String = { $0 ? "✅" : "❌" }
// ------------------------
// Predicative
// ------------------------
protocol Predicative {
static func + (lhs: Self, rhs: Self) -> Self
static func * (lhs: Self, rhs: Self) -> Self
static func gt<P: PredicateLike>(_: P.Input) -> P where P.Input: Comparable, P.Output == Self
static func lt<P: PredicateLike>(_: P.Input) -> P where P.Input: Comparable, P.Output == Self
static func eq<P: PredicateLike>(_: P.Input) -> P where P.Input: Equatable, P.Output == Self
static func or<P: PredicateLike>(_: P, _: P) -> P where P.Output == Self
static func and<P: PredicateLike>(_: P, _: P) -> P where P.Output == Self
static func run<P: PredicateLike>(_: P) -> (P.Input) -> Self where P.Output == Self
}
extension Predicative {
static func or<P: PredicateLike>(_ p1: P, _ p2: P) -> P where P.Output == Self {
return .init(p1, p2, *)
}
static func and<P: PredicateLike>(_ p1: P, _ p2: P) -> P where P.Output == Self {
return .init(p1, p2, +)
}
static func run<P: PredicateLike>(_ p: P) -> (P.Input) -> Self where P.Output == Self {
return p.run
}
}
// ------------------------
// Predicate
// ------------------------
protocol PredicateLike {
associatedtype Input
associatedtype Output: Predicative
init(_: Input, _: @escaping (Input, Input) -> Output)
init(_: Self, _: Self, _: @escaping (Output, Output) -> Output)
func run(_ i: Input) -> Output
}
extension PredicateLike {
static func leaf<P>(_ i: P.Input, _ b: @escaping (P.Input, P.Input) -> P.Output) -> P where P: PredicateLike {
return .init(i, b)
}
static func pred<P>(_ p1: P, _ p2: P, _ f: @escaping (P.Output, P.Output) -> P.Output) -> P where P: PredicateLike {
return .init(p1, p2, f)
}
static func or<P: PredicateLike>(_ p1: P, _ p2: P) -> P {
return P.Output.or(p1, p2)
}
static func and<P: PredicateLike>(_ p1: P, _ p2: P) -> P {
return P.Output.and(p1, p2)
}
static func lt<P: PredicateLike>(_ i: P.Input) -> P where P.Input: Comparable {
return P.Output.lt(i)
}
static func gt<P: PredicateLike>(_ i: P.Input) -> P where P.Input: Comparable {
return P.Output.gt(i)
}
static func eq<P: PredicateLike>(_ i: P.Input) -> P where P.Input: Equatable {
return P.Output.eq(i)
}
}
func || <P>(lhs: P, rhs: P) -> P where P: PredicateLike {
return .or(lhs, rhs)
}
func && <P>(lhs: P, rhs: P) -> P where P: PredicateLike {
return .and(lhs, rhs)
}
func gt<P: PredicateLike>(_ j: P.Input) -> P where P.Input: Comparable {
return .gt(j)
}
func lt<P: PredicateLike>(_ j: P.Input) -> P where P.Input: Comparable {
return .lt(j)
}
func eq<P: PredicateLike>(_ j: P.Input) -> P where P.Input: Equatable {
return .eq(j)
}
// ------------------------
// PRED
// ------------------------
enum Predicate<I, O: Predicative>: PredicateLike {
typealias Input = I
typealias Output = O
case _reduceLeaf(I, (I, I) -> O)
indirect case _reducePred(Predicate, Predicate, (O, O) -> O)
init(_ i: Input, _ f: @escaping (Input, Input) -> Output) {
self = ._reduceLeaf(i, f)
}
init(_ p1: Predicate<I, O>, _ p2: Predicate<I, O>, _ f: @escaping (O, O) -> O) {
self = ._reducePred(p1, p2, f)
}
func run(_ i: I) -> O {
switch self {
case let ._reduceLeaf(j, reduce):
return reduce(i, j)
case let ._reducePred(p, q, reduce):
return reduce(p.run(i), q.run(i))
}
}
}
// ------------------------
// Bool
// ------------------------
extension Bool: Predicative {
static func + (lhs: Bool, rhs: Bool) -> Bool {
return lhs && rhs
}
static func * (lhs: Bool, rhs: Bool) -> Bool {
return lhs || rhs
}
static func gt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == Bool {
return .leaf(i, >)
}
static func lt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == Bool {
return .leaf(i, <)
}
static func eq<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Equatable, P.Output == Bool {
return .leaf(i, ==)
}
}
// ------------------------
// String
// ------------------------
extension String: Predicative {
static func * (lhs: String, rhs: String) -> String {
return lhs + " || " + rhs
}
static func gt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == String {
return .leaf(i, { "\($0) > \($1)" })
}
static func lt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == String {
return .leaf(i, { "\($0) < \($1)" })
}
static func eq<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Equatable, P.Output == String {
return .leaf(i, { "\($0) == \($1)" })
}
}
// ------------------------
// N
// ------------------------
struct N: Predicative {
let s: String
let b: Bool
static func + (lhs: N, rhs: N) -> N {
return N(s: lhs.s + rhs.s, b: lhs.b + rhs.b)
}
static func * (lhs: N, rhs: N) -> N {
return N(s: lhs.s * rhs.s, b: lhs.b * rhs.b)
}
static func gt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == N {
return .leaf(i, { N(s:"(\($0) > \($1) [\(flag($0 > $1))])", b: $0 > $1) })
}
static func lt<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Comparable, P.Output == N {
return .leaf(i, { N(s:"(\($0) < \($1) [\(flag($0 < $1))])", b: $0 < $1) })
}
static func eq<P>(_ i: P.Input) -> P where P: PredicateLike, P.Input: Equatable, P.Output == N {
return .leaf(i, { N(s:"(\($0) == \($1) [\(flag($0 == $1))])", b: $0 == $1) })
}
}
// ------------------------
// Sequence
// ------------------------
extension Sequence {
func filter(by: IPredicate<Element, Bool>) -> [Element] {
return filter(by.run)
}
func filter(by: IPredicate<Element, N>) -> String {
return map { e in
let n = by.run(e)
return "\(flag(n.b)) \(e): \n " + n.s
}.joined(separator: "\n ")
}
}
// ------------------------
// IPredicate
// ------------------------
struct IPredicate<I, O: Predicative> {
let run: (I) -> O
init(_ f: @escaping (I) -> O) {
self.run = f
}
init<P>(_ target: KeyPath<I, P>, _ predicate: Predicate<P, O>) {
run = { i in O.run(predicate)(i[keyPath: target]) }
}
func liftD(_ ip: IPredicate<I, O>, _ f: @escaping (O, O) -> O) -> IPredicate<I, O> {
return IPredicate { i in f(self.run(i), ip.run(i)) }
}
}
func == <R, I, P>(lhs: KeyPath<R, I>, rhs: Predicate<I, P>) -> IPredicate<R, P> where I: Comparable {
return IPredicate(lhs, rhs)
}
func && <I, O>(lhs: IPredicate<I, O>, rhs: IPredicate<I, O>) -> IPredicate<I, O> {
return lhs.liftD(rhs, +)
}
func || <I, O>(lhs: IPredicate<I, O>, rhs: IPredicate<I, O>) -> IPredicate<I, O> {
return lhs.liftD(rhs, *)
}
//// ------------------------
//// Example
//// ------------------------
struct P: CustomStringConvertible {
let name: String
let age: Int
let address: A
var description: String {
return "P(name: \(name), age: \(age))"
}
}
struct A {
let country: String
}
let allPersons = [
P(name: "a", age: 40, address: A(country: "🇨🇦")),
P(name: "a", age: 31, address: A(country: "🇬🇧")),
P(name: "b", age: 21, address: A(country: "🇩🇪")),
P(name: "b", age: 50, address: A(country: "🇫🇷")),
P(name: "c", age: 60, address: A(country: "🇯🇵"))
]
let result1: [P] = allPersons.filter(by:
\.age == eq(40) || \.name == eq("a")
)
let result2: String = allPersons.filter(by:
\.age == eq(40) || \.name == eq("a")
)
print(result2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment