Hi all, I thought I'd post a TL;DR; from this discussion thread to sync up on what has changed and what hasn't:
Changes
We're arguing for the following API changes:
FilePath.init(_ string: String)becomes failable, matching the corresponding inits onAnchorandComponent. The Span-based init becomes failable as well. Both reject input containing NUL.init(stringLiteral:)keeps its trapping behavior for ill-formed literals.resolve()is annotated@available(*, noasync).nullTerminatedCodeUnitsis removed and replaced by a closure-basedwithCString(_:)trackingString.withCString(_:). The body's pointer parameter isUnsafePointer<FilePath.CodeUnit>, so it is wide on Windows. String already establishes viawithCString(encodedAs:_:)that this name is not narrow-only.isRelativeis removed;isAbsoluteremains. There are several distinct kinds of relative on Windows (foo\bar,C:foo\bar,\foo\bar), which a single negated property obscures.
Future work
The proposal's existing Future Directions cover several deferred items already. Based on this thread, we want to clarify or expound on the following:
- The multi-platform path library direction: failable cross-platform conversions and possibly a generic-over-storage path type, distinct from the currency-type role
FilePathis playing here. - Async non-blocking and sync non-blocking variants of
resolve(), both pending a stdlib I/O pool design. - Richer validation API for degenerate path forms. The new failable inits reject only NUL; further validation requires application-specific configuration.
- A
/operator for append, with the component-vs-sequence-vs-join question called out explicitly. - Two language-level wishes that would shape future
FilePathAPI:- a standard story for null-terminated pointer types (e.g. a
var cStringinstead of closure-basedwithCString) - an effects system for blocking I/O that would let
resolve()carry the corresponding annotation.
- a standard story for null-terminated pointer types (e.g. a
Kept, with rationale
resolve() stays in the proposal, synchronous and blocking, marked @noasync. Correct path resolution is hard to get right and easy to get wrong: it interacts with symlinks, .. segments, mount points, Darwin volfs anchors, and Windows reparse points. Without a stdlib implementation from day one, developers will write their own incorrect or racy versions. Async non-blocking is a much needed, but longer-term, variant that depends on an I/O pool. Async+blocking would be worse to ship than sync+blocking, since it would silently block executors.
We're keeping resolve() as the proposed name. We'll add a new Alternatives Considered subsection for the alternative names proposed (resolveByBlocking(), resolveFromFileSystem(), sync/async pairs, namespaced free functions) for LSG visibility. We're still recommending the original name but curious how the LSG views this issue.
Character is kept for separator and driveLetter. The intended usage is more print()-like than low-level parser-like. Code that parses path bytes by hand is already platform-encoding-specific enough to zero-extend the separator (U+002F or U+005C) via separator.utf8.first!. Better support for custom parsers is future work.
Cross-platform parsing of string literals is by design. A POSIX literal parses correctly on Windows and renders with \. Some literal forms remain unavoidably platform-specific, and that is a constraint of multi-platform support rather than a defect of FilePath.
FilePath tracks the target platform under cross-compilation, not the host. It is the path type for the running process, intended to be handed to the OS. Use cases wanting host syntax in macros and plugins are better served by the multi-platform path library above.