New literal for string from contents of file

This is a great idea that solves one of my biggest problems on linux -- there is no way for a unit test to read external files (like json files to parse) without using an absolute path. String literals don't really work because you have to put escapes in them.

I think the relative path should be to the source file, and the absolute path to the project directory. If someone moves a file, you'll get a compile error. Target membership doesn't matter, since it is included at compile time. If you move a file in Xcode, you'll get a compile error if you didn't move the incuded file or change the path in the literal.

I also don't see any reason to worry about encodings. Require it to be utf8. This is part of a source file, not something loaded at runtime. A string variant and a data variant that just returns a byte array should cover all cases, with the convience for string as that would seem to be the most likely case.

This is a feature idea for Swift, not Xcode. Let the Xcode team worry about how to implement it visually.

1 Like

I don't think data literals should be part of the language. We need cross-platform resource management, so maybe this will allow embedded resources (similar to the CREATE_INFOPLIST_SECTION_IN_BINARY setting in Xcode).

I'm going to try to come at this from a different direction.

When you start putting long chunks of data in files, you quickly start running into the need for templating. For instance, your SQL query string needs to support both forwards and backwards sorting. Or the HTML you're generating needs a person's name substituted in. However, we don't want to create a whole templating language from scratch—that's a huge undertaking.

Embedding file names in your code is also a bit fraught. We don't really include any compile-time file names in Swift code currently; it's too big a pain, and the difference between compile-time and runtime locations can be too confusing.

So maybe we should take a different approach here: Instead of having a file name literal syntax, we should make it easier to write functions which merely generate big wads of string data. This would allow much more sophisticated behavior and easier handling for the compiler, at the cost of having a little bit of Swift syntax in the files and giving them a .swift extension.

So with this design, the execLongQuery() function would look more like:

func execLongQuery() {
   let sql = makeLongQuery()
   database.execute(sql)
}

And the actual query definition might look like:

func makeLongQuery() -> String """
    SELECT events.id, events.created_at, ...
      FROM events, ...
     WHERE ...
    """

If you wanted to support changing the sort order, that might look like:

func makeLongQuery(ascending: Bool = true) -> String """
    SELECT events.id, events.created_at, ...
      FROM events, ...
     WHERE ...
     ORDER BY events.created_at \(ascending ? "ASC" : "DESC")
    """

If you ever want to use more complicated logic than can easily fit into an interpolation…well, refactor it into a function with a normal body. It's called in exactly the same way.

A function like this could be declared to return any ExpressibleByStringLiteral type. For example, if you had a "safe SQL statement" type or a "safe HTML fragment" type with automatic escaping, you could declare your function to use those instead.

And as for supporting Data values? Conform Data to ExpressibleByStringLiteral and you can declare your function to return Data instead of String.

A #dataLiteral(resourceName:) would likely be used with NSDataAsset in AppKit and UIKit.

On other platforms, it could be used with a third-party NSDataAsset type for source compatibility.

That's a good insight. I'll admit, I haven't been thinking about interpolation at all thus far. I agree, a templating language is not something we should build into Swift. I guess without direct interpolation you'd have to pipe the content through String.init(format:), which is okay.

Either way, I think that's a red herring. This proposal has become more about embedding any data, not just strings. I should think more on this and perhaps pick a different example as my motivating use case. Chris Lattner suggested there may be some interesting uses around graphics programming (CUDA), I think.

Foundation supports the "data" URL scheme and Base-64 encoded strings.

Smaller data assets could be stored as string constants in the binary.

I'd much rather have a data literal from file than a string from file.

1 Like

omg i would love being able to have GLSL shaders be literals inside my swift code. and you wouldn’t really need templating for that. but it shouldn’t be tied to a specific Foundation API it should just be a String

3 Likes

Random opinions:

I agree with Erica and others that this proposal make sense to focus on data rather than strings. The later can be constructed from the former :-)

I'm personally not too concerned by the load time issues: modern OS's lazily page in the contents of read-only data sections.

I'm also not too worried about the usability issues of "drag files around in Xcode and have references break". I'm not worried for two reasons: on the one hand, this is already an area of fragility in Xcode (sigh) and on the other hand, Xcode does would have exactly the information it would need to update these references. Also, if the code was broken, it would be a build error when the compiler tried to handle this, so there isn't a danger of the code being broken in a subtle way.

-Chris

3 Likes