[Pitch] #Expression Macro and Type

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.

10 Likes