Swift accepts typed results for regular expressions
SE-0350 gives regular expressions a generic output type and a matching model aligned with Swift strings. Its acceptance establishes a foundation while leaving related syntax proposals separate.
A regular expression can identify the fields in a bank statement without producing values that application code can use. Captured text still needs interpretation, and the caller has to remember which capture represents which field. Swift's accepted regex design brings those responsibilities into a typed matching model. A match can describe its captures through the output type, while related builder work can make conversion part of matching itself.
The May 3 acceptance of SE-0350 establishes the Regex type and an overview of the larger effort. The decision follows a second review. It mentions revised argument labels and checking for task cancellation during potentially long-running synchronous matching. Acceptance is a language design milestone, and should not be read as a declaration that every related regex feature is available in the current stable compiler.
I like Swift's habit of representing meaningful distinctions in types. Regular expression captures are a good place to apply that habit because an integer capture index says little about the value it is supposed to identify. A type that records the output gives the rest of a program more information to work with.
Matching and interpreting belong together
Michael Ilseman's SE-0350 proposal motivates the design with a statement containing transaction kind, date, account name and amount. Fields are separated by multiple spaces, while account names may themselves contain spaces. Splitting at every whitespace character loses a distinction that the input format actually needs.
A regular expression can encode those boundaries. With Foundation's NSRegularExpression, however, the proposal's example still has to retrieve numbered ranges and convert each captured field. Finding text that looks like a date and parsing it as a date are separate phases. The regex can accept overly broad input, leaving the application to reject it later.
The new design gives a regex an Output parameter. Its match result exposes that output, including capture values. Related builder proposals provide a way to transform captures and incorporate existing parsers. That would let a parser's failure participate in the matching process, so the engine can try an alternative when a candidate field cannot produce the required value.
This changes where validation happens. If a broad text match consumes something that later fails conversion, application code has to decide how to recover. If conversion is part of the matching algorithm, failure has a defined place in the engine's control flow. The proposal describes a failable transformation returning nil as local failure, with backtracking then trying another possibility.
That is especially useful for structured fields whose real grammar is already implemented by a library. A date parser can supply date semantics instead of asking a regular expression to approximate every valid date and then repeating the validation elsewhere. SE-0350 presents this as cooperation between string matching and parsers, with the detailed integration delegated to related proposals.
Output can be static or dynamic
Runtime construction remains part of the design. A search field or command-line filter can supply a pattern that the program does not know at compilation. The proposal gives that construction a dynamic output representation, AnyRegexOutput, when no more specific output type is supplied.
Patterns known in source can support statically known captures. The overview presents an explicit output signature for runtime construction as well, with mismatched types and invalid patterns producing construction errors. These are different sources of certainty. A declared output signature asks runtime construction to verify compatibility, while a source literal can give the compiler the pattern itself.
The following declaration illustrates the accepted generic model. It describes the output type without relying on the separate literal delimiter design.
// Proposed Regex API, not a Swift 5.6 example.
let regex: Regex<(Substring, Substring)> = try Regex("a(.*)b")
By convention, the first component represents the complete match, and subsequent components represent captures. The pattern above therefore describes one complete matched substring and one captured substring. Keeping the full match in the output preserves the familiar relationship between capture zero and the overall match.
The proposal considers separating the complete match from captures into different generic parameters. It rejects that presentation in part because capture numbering would become less familiar and could introduce index mistakes. The selected output convention makes the tuple's positions correspond to the pattern's numbering.
Named captures and references are also part of the overview, although their detailed designs belong to the associated syntax and builder proposals. A match supports access to its output, and the type-level representation can support completion and other source tools. The benefit comes from recording capture structure before a caller starts indexing into the result.
Unicode determines what a character match means
Swift strings do not treat a user-visible character as necessarily one code unit. SE-0350 describes Character as an extended grapheme cluster and string equality in terms of canonical equivalence. A regex intended to fit Swift's string model has to account for those choices in its own matching rules.
The proposal makes a dot match an extended grapheme cluster by default. Canonically equivalent text also matches by default. Those choices connect a regex operation to the character-level operations a Swift programmer already uses on String. They avoid introducing an unexplained change of text model merely because an operation moved into regex syntax.
This is a substantive distinction from the Foundation wrapper discussed in the proposal. NSRegularExpression uses ICU's engine, whose behaviour can split grapheme clusters and does not provide the same canonical-equivalence comparison described for Swift strings. A pattern's apparent simplicity therefore does not establish that it behaves the same under both models.
The overview also allows scalar-level processing and compatibility character classes through related work. Character-level defaults do not mean lower-level views are inappropriate. They mean the default should be coherent with Swift's native text model, while a different processing requirement needs an explicit choice.
For me, that consistency matters as much as shorter capture access. A text operation is difficult to reason about when switching APIs silently changes the unit of matching. Making the default agree with ordinary string operations reduces one source of that discrepancy.
Matching has observable control flow
The design treats a regex as a string-processing algorithm with ordered choice. Alternative order matters when more than one alternative can match. SE-0350 also discusses effects inside capture transformations. A transformation containing a print statement must execute according to the algorithm's matching attempts, unless an optimization can prove that omitting it preserves behaviour.
That contract makes builder transformations part of program execution. They are not merely annotations on a recognizer. A developer has to account for the possibility that backtracking invokes a transformation during an attempt that does not become the final match. The design's explicit execution model supplies a basis for reasoning about that behaviour.
Potentially long-running processing also explains the acceptance decision's cancellation change. A synchronous API can still perform enough work that an enclosing task's cancellation matters. The accepted direction allows regex algorithms to check whether the parent task has been cancelled and terminate execution. That does not turn matching into an asynchronous operation or establish a fixed upper bound on its duration.
The accepted foundation leaves separate decisions open
SE-0350 organizes the overall effort into related proposals, including runtime pattern syntax, literals, builders and Unicode behaviour. It explicitly states that accepting the type and overview does not require accepting every related proposal. The examples explain how a complete system could fit together, while the proposal boundaries preserve separate review of each part.
That separation matters for adoption claims. The acceptance settles a generic regex output model and the role of match results. It supplies an account of how dynamic patterns and typed captures can coexist. A particular literal spelling, builder API or string algorithm still needs its own design and implementation status checked before it belongs in production source.
The output convention also has a concrete implementation consequence. The proposal describes matches as storing ranges, with captured substrings created lazily when requested. Representing captures in a type does not require allocating a separate string for every field as soon as matching finishes. The design can preserve references into the original input while giving callers structured access to the fields they actually need.
The accepted API distinguishes matching an entire input, matching its prefix and finding a first match within it. Those operations answer different questions about surrounding text. An input validator can require a whole match, while a search operation can accept a matching region inside a larger string. Selecting the operation remains part of specifying the intended parser, even when the capture types already describe its result.