Skip to content

Instantly share code, notes, and snippets.

@Segmentational
Created September 28, 2025 22:41
Show Gist options
  • Save Segmentational/3c1ece052fc73e01ab6933f6126bbd07 to your computer and use it in GitHub Desktop.
Save Segmentational/3c1ece052fc73e01ab6933f6126bbd07 to your computer and use it in GitHub Desktop.
Swift-Prose-Expression-Predicate.swift
//
// Prose.swift
// Swift-Predicates
//
// Created by Segmentational on 9/28/25.
//
import Foundation
/// A predicate-based expression.
///
/// **Prose**: *Written or spoken language in its ordinary form, without metrical structure.*
///
/// - Note: The underlying structure of a `Predicate` is composed of `PredicateExpressions` objects. These
/// represent the individual components and operations within the predicate's logical condition, providing type safety
/// at compile time.
protocol Prose: Sendable {
func prose() -> String
}
extension String: Prose {
func prose() -> String {
"String"
}
}
// Repeated for each supported operator.
extension PredicateExpressions.Equal: Prose where LHS: Prose, RHS: Prose {
func prose() -> String {
return """
(\(String(lhs.prose())) EQUALS \(rhs.prose()))
"""
}
}
// MARK: - PredicateExpressions Prose Conformances
// Render literal values like "AAPL" or 210.0
extension PredicateExpressions.Value: Prose {
func prose() -> String {
"\(value)"
}
}
//extension PredicateExpressions.Value: Prose where Output == Double {
// func prose() -> String {
// "\(value)"
// }
//}
// Render key paths like Example.symbol or Example.price
extension PredicateExpressions.KeyPath: Prose {
func prose() -> String {
let base = String(String(reflecting: keyPath).split(separator: ".").first ?? "").dropFirst()
let property = String(String(reflecting: keyPath).split(separator: ".").last ?? "")
return """
\(base).\(property)
"""
}
}
struct Example: Sendable, Hashable, Equatable, Codable {
var symbol: String
var price: Double
}
// MARK: - Predicate Convenience
extension Predicate {
func prose() -> String? {
guard let expression = expression as? Prose else { return nil }
return expression.prose()
}
}
#if DEBUG && canImport(Playgrounds)
import Playgrounds
#Playground {
// MARK: - Examples
let p1: Predicate<Example> = #Predicate {
$0.symbol == "AAPL"
}
let p2: Predicate<Example> = #Predicate { q in
q.price == 210.0
}
let p3: Predicate<Example> = #Predicate { q in
q.price == 220.0
}
// Prints the prose representation
print(p1.prose() ?? "No prose for p1")
print(p2.prose() ?? "No prose for p2")
print(p3.prose() ?? "No prose for p2")
}
#endif
// MARK: - Swift 6 Concurrency Shims
// Some toolchains don't yet mark KeyPath types as Sendable even when Root/Value are Sendable.
// The #Predicate macro expansion can require these to be Sendable. Provide local, conditional
// conformances to unblock builds under strict concurrency. These are safe because KeyPath values
// are immutable and thread-safe by design. We still mark them @unchecked to reflect the retroactive nature.
#if swift(>=6)
extension KeyPath: @unchecked @retroactive Sendable where Root: Sendable, Value: Sendable {}
extension WritableKeyPath: @unchecked Sendable where Root: Sendable, Value: Sendable {}
extension ReferenceWritableKeyPath: @unchecked Sendable where Root: Sendable, Value: Sendable {}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment