Hi all,
I'd like to pitch some new APIs for Foundation as an expansion of our existing #Predicate
macro and type that we introduced last year. Please feel free to read the details below and let me know if you have any comments/suggestions!
#Expression
Macro and Type
- Proposal: SF-NNNN
- Author(s): Jeremy Schonfeld
- Review Manager: TBD
- Bug: rdar://122026982
- Status: Pitch
- Implementation: coming soon
Introduction
Last year, we introduced the new #Predicate
macro and its associated type Predicate
. This macro allows developers to craft boolean expressions via standard swift closure syntax in order to create Sendable
, serializable, and introspectable predicates for use in higher level APIs such as SwiftData. Since Predicate
represents a boolean expression, its evaluate function simply returns a Bool
value and its contained expression is constrained to an output type of Bool
. However, there are some additional situations in which developers may need to create these serializable/introspectable expressions to reference values that do not produce booleans but rather produce arbitrary types.
Motivation
There are a few cases where expressions not constrained to a boolean output are helpful. For example, developers may wish to fetch specific values from a model/row in a database rather than the entire model (this is especially helpful when fetching small pieces of information from large models without paging the full model into memory). Predicate
is extremely useful in filtering which rows should be selected, but selecting just a singular field (or combination of fields) from a row is not possible when introspectable query pieces are constrained to boolean outputs. Instead, developers may wish to express that only a certain keypath (or multiple keypaths concatenated together) should be selected from a model in the database. As a concrete example, see the NSFetchRequest.propertiesToFetch
API. Additionally, non-boolean expressions can be helpful when producing other types of mappings such as mapping properties of a model/row in a database into another shape (such as a spotlight entity). Non-boolean expressions can be used to represent mappings from one model/entity's properties to a different model/entity's properties. For example, developers may wish to use natural swift expressions to represent mapping a model to a CSSearchableItemAttributeSet
.
Proposed solution and example
In order to support these types of APIs in SwiftData and other frameworks, I propose adding a new #Expression
macro and associated Expression
type. This type will closely mirror the API of Predicate
(and will share a lot of the same implementation details and support APIs) but will be generic over the output type rather than constraining the output type to Bool
. For example, developers would be able to represent the following:
// Example models
class Library {
var albums: [Album]
}
class Album {
var contents: [Photo]
var isHidden: Bool
}
let libraryAlbumCountExpression = #Expression<Library, Int> { library in
library.albums.filter {
!$0.isHidden
}.count
}
// Evaluate in-memory or pass to some API
let numberOfAlbums = try libraryAlbumCountExpression.evaluate(someLibrary)
For more details including the detailed design, check out the full pitch here.