[swift-evolution] URL Literals

Xiaodi Wu xiaodi.wu at gmail.com
Mon Dec 19 13:42:15 CST 2016


On Mon, Dec 19, 2016 at 1:40 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:

> On Mon, Dec 19, 2016 at 1:18 PM, Tony Allevato <allevato at google.com>
> wrote:
>
>> +1 to the sentiment in your last paragraph.
>>
>> In general, I'm not a very big fan of the #foo(...) syntax for literals
>> and I think using that as the starting point for discussion biases us
>> towards those when more general and powerful alternatives could exist. That
>> syntax exists for Playground support and I'd hate to see it proliferate
>> into non-playground sources. Furthermore, if the user could write a
>> compile-time-validating initializer for `URL("http://foo.com/bar")` (or
>> `let url: URL = "http://foo.com/bar"`), then that expression becomes
>> just as much a "literal" as something like `#url("http://foo.com/bar")`.
>> There's no functional difference between the two: both are sequences of
>> tokens that the compiler can evaluate statically. The benefit is that the
>> end user doesn't have to worry about the distinction; the compiler picks
>> the appropriate evaluation automatically depending on whether the
>> initializer argument is statically known or not.
>>
>
> +1 to this. If we could mark an initializer for URL as a constexpr that
> takes a string literal, then conformance to ExpressibleByStringLiteral
> would make `let foo = URL("http://example.com")`
>

Rather, I meant `let foo: URL = "http://example.com"`.


> work exactly as desired at compile time, and this is IMO the most elegant
> solution.
>
> Taking this a step further, if we can get constexpr-like validation for
>> initializers like this, then I wonder if the #foo literal syntax needs to
>> exist at all, and I'd propose that we abandon it and just let the
>> playground UI and other IDEs transform things like `UIColor(red: 0.5,
>> green: 0.5, blue: 0.5)` into a swatch when it sees them if they can be
>> evaluated at compile-time. What you lose is, as Erica mentioned earlier,
>> the fact that `#colorLiteral(...)` is untyped and can be turned into
>> multiple color types, but the value already has to be assigned to a
>> concrete type eventually anyway.
>>
>> Am I missing any use cases for things like #colorLiteral that doing this
>> would make difficult or impossible?
>>
>>
>> On Mon, Dec 19, 2016 at 10:53 AM David Sweeris via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>>
>>> On Dec 19, 2016, at 1:26 AM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>>
>>> URLs are unlikely to be something that can be validated by regex. See,
>>> for instance, this discussion: <https://webkit.org/blog/7086/
>>> url-parsing-in-webkit/>. The full spec is here: <
>>> https://url.spec.whatwg.org>. If Swift were to implement parsing of
>>> URLs at the level of the compiler or core library, I'd expect it to be the
>>> full spec, as we do with Unicode.
>>>
>>>
>>> On Mon, Dec 19, 2016 at 2:26 AM, Benjamin Spratling via swift-evolution
>>> <swift-evolution at swift.org> wrote:
>>>
>>> Howdy,
>>> Yes, I was also intrigued by the “Regex” validation mentioned in another
>>> post.  It could offer a convenient way to get some literals support in
>>> without the headaches associated with the constexpr C++ approach.
>>> I’m curious, though, how many types can we image in can be validated by
>>> this method?  If it really is just URL’s, then I’d actually lean towards
>>> making this a compiler magic feature.
>>>
>>> Someone else mentioned fetching the URL’s for a preview.  Given that we
>>> might be coding “deletes” in URL’s (yes, I recently met a backend developer
>>> who coded a delete as a GET), I really highly suggest we not ping people’s
>>> API’s artificially.  At least we shouldn’t for non-file-scheme URLs.  IMHO,
>>> verifying that a service is active isn’t really the Swift compiler’s job.
>>> It might happen as part of coordinated run-time tests, which sometimes have
>>> to be balanced to keep test data correct, something the IDE wouldn’t know
>>> how to enforce correctly.
>>> -Ben
>>>
>>> On Dec 19, 2016, at 1:41 AM, David Sweeris via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>>
>>>
>>> On Dec 17, 2016, at 1:12 PM, Micah Hainline via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>>
>>> I'd love a fleshed out elegant example for URL that shows what a
>>> complete implementation of that special init method would look like.
>>>
>>>
>>> Sorry this took so long… the weekend kinda got away from me.
>>>
>>> Anyway, I was thinking something like this (which has been very
>>> simplified on account of my regexing being sub-sketchy, and me not knowing
>>> exactly what’s valid in an URL anyway):
>>>
>>> #literalpatterns += (name: “URLLiteralType”, components: (name: url,
>>> type: StringLiteralType, pattern: “(http|https)://(www.)?[a-z|A-Z|0-9]+
>>> .(com|org|net)(/[a-z|A-Z|0-9]+)*(/[a-z|A-Z|0-9]+.[a-z|A-Z|0-9]+)?”),
>>> protocol: ExpressibleByURLLiteral)
>>>
>>> This would let the compiler know pretty much everything it needs to
>>> know… that the “new” type is called “URLLiteralType", that it starts out
>>> life as young StringLiteralType with a bright future in the computer
>>> industry, that in order to succeed it has to match a given pattern, and
>>> what protocol a type has to conform to in order to use an URLLiteral. In
>>> practice, the compiler would synthesize a struct containing the specified
>>> members and validate the literal with the specified pattern before making
>>> an “instance” of it (since we’re talking about literals and compile-time
>>> code here, I’m pretty sure that “instance" the wrong terminology… pardon my
>>> ignorance)
>>>
>>> struct URLLiteralType: {
>>>     let url: StringLiteralType
>>> }
>>>
>>> A tuple would be better, IMHO, but according to the playground,
>>> single-element tuples can’t have element labels. As for the
>>> implementation of the init function:
>>>
>>> init(urlLiteral value: URLLiteralType) {
>>>     let urlString = value.url
>>>     //Do whatever URL is doing now, except there’s no need to check for
>>> errors since the compiler pre-validated it for us
>>> }
>>>
>>>
>>> If it’d be more useful, the pattern could be split into multiple pieces:
>>>
>>> #literalpatterns += (name: “URLLiteralType”,
>>>                      components: ((name: “`protocol`", type:
>>> StringLiteralType, pattern: “(http|https)”),
>>>                                   (name: _,            type:
>>> StringLiteralType, pattern: “://”),
>>>                                   (name: “domain",     type:
>>> StringLiteralType, pattern: “(www.)?[a-z|A-Z|0-9]+.(com|org|net)”),
>>>                                   (name: “path”,       type:
>>> StringLiteralType, pattern: "(/[a-z|A-Z|0-9]+)*(/[a-z|A-Z|0-9]+.
>>> [a-z|A-Z|0-9]+)?”))
>>>                      protocol: ExpressibleByURLLiteral)
>>>
>>> This would result in URLLiteralType looking like this:
>>>
>>> struct URLLiteralType: {
>>>     let `protocol`: StringLiteralType
>>>     let domain: StringLiteralType
>>>     let path: StringLiteralType
>>> }
>>>
>>> And in the init would start out like this:
>>>
>>> init(urlLiteral value: URLLiteralType) {
>>>     let protocolType = value.protocol
>>>     let domain = value.domain
>>>     let path = value.path
>>>     //Do whatever with the components
>>> }
>>>
>>>
>>> The “base” types of literals like Int or String that don’t refine
>>> pre-existing literal types would still need a bit of compiler magic (or at
>>> least a different mechanism for becoming actual types), but as long as
>>> a type doesn’t take advantage of reference semantics in its stored
>>> properties or something, I *think* pretty much any data type could become
>>> “literalizeable” with something like this. Oh, and there’s nothing
>>> particularly magical about regular expressions as far as this idea is
>>> concerned; they’re just usually the first thing that comes to mind when I
>>> think of pattern matching in a string.
>>>
>>> I know this looks like a lot of code, but the scary-looking parts with
>>> the regex stuff only has to be written once for each “type” of literal…
>>> types that want to be expressible by such a literal just have to write an
>>> init function.
>>>
>>>
>>> It doesn’t have to be regex per se… instead of
>>> #literalpatterns += (name: “URLLiteralType”, components: (name: url,
>>> type: StringLiteralType, pattern: “(http|https)://(www.)?[a-z|A-Z|0-9]+
>>> .(com|org|net)(/[a-z|A-Z|0-9]+)*(/[a-z|A-Z|0-9]+.[a-z|A-Z|0-9]+)?”),
>>> protocol: ExpressibleByURLLiteral)
>>> I probably should’ve written something more like:
>>> #literalpatterns += (name: “URLLiteralType”, components: (name: url,
>>> type: StringLiteralType, matching: Regex(“(http|https)://(ww
>>> w.)?[a-z|A-Z|0-9]+.(com|org|net)(/[a-z|A-Z|0-9]+)*(/[a-z|A-Z|0-9]+.
>>> [a-z|A-Z|0-9]+)?”)), protocol: ExpressibleByURLLiteral)
>>> where the `matching` argument can be anything that can (“@purely”-ly)
>>> use some specified mechanism (I’d vote for the ~= operator) with a literal
>>> to test whether it matches. Also, there is no existing `Regex`
>>> struct/class/mechanism in Swift, unless you count `NSRegularExpression`. I
>>> didn’t want to use that for a couple reasons… 1) I don’t think it’s part of
>>> the stdlib, and 2) it doesn’t have a non-failable init that just takes a
>>> string, so using it unmodified would kinda put us in a “it’s turtles all
>>> the way down” kind of situation. What I’d started doing was to look for the
>>> existing mechanism for specifying literals in the compiler so I could use
>>> the existing name for it (somehow I doubt there’s a actually an array of
>>> patterns called “literalpatterns" in the compiler) and copy the existing
>>> methods for specifying a valid literal. After being unsuccessful for some
>>> amount of time, I decided I was getting too tired and made up what I sent
>>> last night.
>>>
>>>
>>>
>>> The more I think about it, the more I’m starting to be of the opinion
>>> that we really ought to have two mechanisms here… One for specifying what
>>> constitutes a “base” literal (like `43`, `[“foo”, “bar”]`, or `true`), and
>>> one for types that merely need to perform some sort of validation on
>>> existing “base” literals. The first mechanism probably should be fairly
>>> arcane and involved, because you’d essentially be able to create new
>>> syntaxes, which *should* be kinda scary and hard to understand because
>>> it’s most certainly not an area beginners should be in. The second
>>> mechanism — something like that `ExpressibleByValidatedStringLiteral`
>>> idea — isn’t nearly as complicated. In the case of URLs, I’d vote the
>>> second approach. We only really need two extra features to implement it
>>> (“@constexpr” and the compiler being able to use the REPL to evaluate
>>> @costexpr statements), and both of them have more uses other than just
>>> getting a few more compile-time checks or allowing for more inits to be
>>> non-failable. With both of those in place, getting an url “literal” becomes
>>> just this:
>>> protocol ExpressibleByValidatedStringLiteral {
>>>     init?(stringLiteral value: StringLiteralType)
>>> }
>>> struct URL : ExpressibleByValidatedStringLiteral {
>>>     //Stuff
>>>     //...
>>>     init?(stringLiteral value: StringLiteralType) {
>>>         //Perform validation here; return nil if it fails
>>>     }
>>>     //...
>>>     //Things
>>> }
>>> var lru: URL = "foo" //Compiler throws this to the init? function, it
>>> returns nil, the compiler raises a syntax error
>>> var url: URL = "http://www.some.valid.url.com" //Compiler throws this
>>> to the init? function, it returns an optional URL, the compiler unwraps it
>>> and does the assignment
>>>
>>> I still very much want a way to define custom literals (precisely
>>> because it’d let me make new syntaxes), but I’m starting to think that
>>> something like the second, disappointingly easy idea, is probably the way
>>> to go in this case.
>>>
>>> - Dave Sweeris
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20161219/88daf003/attachment.html>


More information about the swift-evolution mailing list