"file:///usr/bin/swift"
is not a file path, so this would conflict with FilePath
's own string literal support.
And having FilePath
's string literal initialisers support both URLs and file paths would be ambiguous on Windows: in "x:/foo" as FilePath
, is x:
a Windows drive letter or a URL scheme? If the former, this should succeed; if the latter, this should fail (because you clearly cannot create a file path from a non-file URL).
IMO, StaticString
initialisers aren't really worth the fuss. I know I suggested ExpressibleByStringLiteral
in the previous pitch, but that was more of a "if you've decided to do A, you might as well do B" comment; not an endorsement that I think A is actually a good idea in the first place.
I did, in fact, consider it a long time ago for WebURL, but I don't really think it's what people really want -- it looks like a feature that people have occasionally asked for (compile-time checking), but it really isn't. It looks so close that IMO, it's misleading to the point where it could actually be harmful.
What's the use-case?
Firstly, it is very rare that you make a request directly from a pure string literal. I can't remember the last time I saw code which actually looked like this:
URLSession.shared.data(from: "https://example.com/file", delegate: nil)
Most of the time, you will be writing something like this instead. I'm not saying there is no benefit in any of these cases, but let's be honest... it's marginal at best.
var endpoint = URL("https://api.example.com/...")
var endpoint = "https://api.example.com/..." as URL
var endpoint: URL = "https://api.example.com/..."
The most compelling use-case I can think of, where people might actually be making requests from string literals is in scripts. Building a request API optimised for scripting is interesting, but I think it probably wouldn't look much like URLSession, and would probably include an overload for specifying URLs as non-static Strings anyway (script-centric APIs are all about getting the type system out of the way for the sake of convenience).
But doesn't it help you write correct code?
So what do we actually gain? Basically, the ability to omit the !
force-unwrap. And to be clear - as mentioned in the pitch, it does not remove any runtime errors or crashes, or even catch a single bad URL earlier than you could today with the force-unwrap. It just hides them. That's literally all it does.
There are Swift developers who make it their mission to avoid all force-unwraps, even though the community has repeatedly discussed it and found that whole philosophy to be a fundamental misunderstanding of the language. It tends to come from the idea that they want their apps to never crash - which is understandable, of course nobody wants their apps to crash. But lots of functionality in Swift can fail at runtime (e.g. accessing an invalid Array index), and the only way to fully guard against it is to test your code (as it always was). Stuff like this will still fail at runtime if you don't test all code paths:
let endpoint: URL
if userLocaleIsFrench {
endpoint = "https://api.example.com/..."
} else {
endpoint = "https://api.fr .example.com/..." // whoops! typo!
}
Again - this feature would just hide the potential crash, not remove it. Is that really a better situation? Some might say yes, others may disagree; what I think we should be able to accept is that the benefits (if there are any) are not obvious. It is not a clear improvement. Is it obvious by looking at that code that it will fail at runtime?
It is true - things that are ExpressibleByStringLiteral
, or which include non-failable StaticString
initialisers, do not need to accept every string value, and it is within the protocol's semantics for them to fail at runtime. However - the reason people ask for this feature is because they want a particular failure behaviour, which this initialiser can't and won't provide. They do sometimes find typos in URLs, and they want those to be flagged even in branches which are not actually tested (which... , really - there's no substitute for tests). That is something that only build-time checking can provide. This thing gives you a false sense of security; it's just as misguided as those who proclaim that all force-unwraps are evil or "code smells".
FWIW, the approach I'm planning to take for WebURL is to create a linting plugin. It hasn't been a priority so far, but my idea is to either create one of the new-fangled command plugins, or add some kind of integration with an existing linting tool. It's not compile-time checking, so it still requires an extra command/tool, but it is build-time checking, so IMO it's closer to what people actually want.