-
-
Notifications
You must be signed in to change notification settings - Fork 96
String Interpolation #140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
String Interpolation #140
Changes from all commits
8b3be64
f9ba6f3
066b9c9
2e727e1
ad74cdd
6c28dba
5ce9b88
261ef14
dd797a2
97c4f0a
4c929d6
f9502f4
800bf97
0d14031
ce8aa9f
7d363af
3569537
1a10e99
35fb28e
88b141c
83f98cf
cfcc96d
e8b8c58
add9e91
4e42bfe
553076d
ad4ebbf
f3ae2a0
cbd5122
ebfc529
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| # String Syntax for Compile-Time Sequences | ||
|
|
||
| | Field | Value | | ||
| |-----------------|-----------------------------------------------------------------| | ||
| | DIP: | (number/id -- assigned by DIP Manager) | | ||
| | Review Count: | 0 (edited by DIP Manager) | | ||
| | Author: | Jason Helson | | ||
| | Implementation: | https://git.io/fpSUA | | ||
| | Status: | Will be set by the DIP manager (e.g. "Approved" or "Rejected") | | ||
|
|
||
| ## Abstract | ||
|
|
||
| This DIP proposes adding a "string sequence literal" to D, primarily inspired | ||
| by string interpolation, but also applicable to a wide variety of use cases. | ||
|
|
||
| In a nutshell, this literal: | ||
|
|
||
| ```` | ||
| i"Hello, ${name}! You have logged on ${count} times." | ||
| ```` | ||
|
|
||
| is translated to the [compile-time sequence](https://dlang.org/articles/ctarguments.html): | ||
|
|
||
| ````D | ||
| "Hello, ", name, "! You have logged on ", count, " times." | ||
| ```` | ||
| Note that the compiler does not perform any string interpolation; it merely | ||
| segments the literal into a sequence of strings and expressions. The intent is for | ||
| further processing to be done in library code (see [rationale](#rationale) for a more detailed | ||
| description of possible applications). | ||
|
|
||
| ### Reference | ||
|
|
||
| - Exploration: https://github.com/marler8997/interpolated_strings | ||
| - Example Library Solution: https://github.com/dlang/phobos/pull/6339/files | ||
| - Implementation: https://github.com/dlang/dmd/pull/7988 | ||
| - https://forum.dlang.org/thread/khcmbtzhoouszkheqaob@forum.dlang.org | ||
| - https://forum.dlang.org/thread/c2q7dt$67t$1@digitaldaemon.com | ||
| - https://forum.dlang.org/thread/qpuxtedsiowayrhgyell@forum.dlang.org | ||
| - https://forum.dlang.org/thread/ncwpezwlgeajdrigegee@forum.dlang.org | ||
| - https://dlang.typeform.com/report/H1GTak/PY9NhHkcBFG0t6ig (#3 in "What language features do you miss?") | ||
|
|
||
| ## Contents | ||
| * [Rationale](#rationale) | ||
| * [Description](#description) | ||
| * [Language vs Library Feature](#language-feature-vs-library-feature) | ||
| * [Breaking Changes and Deprecations](#breaking-changes-and-deprecations) | ||
| * [Copyright & License](#copyright--license) | ||
| * [Reviews](#reviews) | ||
|
|
||
| ## Rationale | ||
|
|
||
| Sequence literals apply to a wide range of use cases. A few of these use cases are outlined below. | ||
|
|
||
| #### String Interpolation | ||
| One notable use for sequence literals is in string interpolation, which allows for more concise, readable, and maintainable code. For example: | ||
|
|
||
|
|
||
| src/build.d:556:<br> | ||
| `auto hostDMDURL = "http://downloads.dlang.org/releases/2.x/"~hostDMDVer~"/dmd."~hostDMDBase;`<br> | ||
| Becomes:<br> | ||
| `auto hostDMDURL = i"http://downloads.dlang.org/releases/2.x/$hostDMDVer/dmd.$hostDMDBase".text;`<br> | ||
| And, with syntax highlighing:<br> | ||
|  | ||
|
|
||
|
|
||
| src/dmd/json.d:1058:<br> | ||
| ``s ~= prefix ~ "`" ~ enumName ~ "`";``<br> | ||
| Becomes:<br> | ||
| ``s ~= i"prefix`$enumName`".text;``<br> | ||
| With syntax highlighting:<br> | ||
|  | ||
|
|
||
|
|
||
|
|
||
| #### Database Queries | ||
|
|
||
| `db.exec("UPDATE Foo SET a = ?, b = ?, c = ?, d = ? WHERE id = ?", aval, bval, cval, dval, id);`<br> | ||
| Becomes:<br> | ||
| `db.exec(i"UPDATE Foo SET a = $(aval), b = $(bval), c = $(cval), d = $(dval) WHERE id = $(id)");` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not equivalent, the implementation of
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed, and actually, I would never, as a library author, write a library function that works with What would stop you from writing But what about It translates to I hate to be the downer, cuz I basically like this proposal, but I still think we should be making this thing give a new type. Instead of a naked tuple, do an anonymous struct. Change `i"foo $(a)".tupleof.whatever is still an option... but then library authors who actually want to work with the details have them available. (and we should probably change text and writeln so those just work first anyway.)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Amen. We need to be able to tell on the callee side if an interpolated sequence was passed vs just variadic args. And if it's a struct we can also have a
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, I can live with the convenience I am slightly disgusted at having a magic struct created by the compiler include such a reference, but I think it works and the compiler already magically references std.math in syntax, so I can live with it. I'd just make its magic I think I will write more about this in today's "this week in D"; I need to write that in a couple hours anyway and that gives me some content to dual-purpose since i missed most of dconf lol I'll link it back here in a while. It might be a fairly different implementation though, since a magic struct cannot - I think anyway - be done with just a lexer hack. Even if you wrapped it in a call syntax to punt the details to the runtime library, you'd actually lose some functionality. (consider: Anyway, I'll write more in a few hours.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, since we have writeln(i"1 + 2 is $(1+2)");
// don't need to import std.conv.text because .text is just a member of the
// interpolated string type
audo s = "1 + 2 is $(1 + 2)".text;I'd like to know where @WalterBright and @andralex stand on this. Since they are the ones to decide what design will be accepted, I'd like to get their input on this particular aspect. But I think I agree with you on this one.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My mistake, I forgot that In any case I do see alot of versatility in using the structure. It's a very good idea and I'm on board with it. It does make a few use cases harder by sometimes requiring the user to use This would mean I don't think we could just use a wrapper, but would need to add a And yes, having a Thanks for taking the time to write this up, I like it alot. I'll think about it and may update my implementation to it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One more comment on your interpolation proposal. I think a big use case for interpolated strings is to make mixins much easier to write/maintain. Using There's 2 ways you could go about string interpolation. You could either parse the interpolated string at the same time you are you are parsing the string literal, or you could first parse the string literal and then interpolate it afterwards. I opted for the second design. With this model, it's easier to determine what will happen in cases like yours because the string is processed by each part one at a time. So in your example: mixin(iq{
string s = i"foo $(bar)";
});
mixin(__d_interpolatedString(`
string s = i"foo `, bar, `";
`));
mixin(`
string s = i"foo barvalue";
`)); string s = i"foo barvalue";
string s = __d_interpolatedString("foo barvalue");Now if you wanted the other behavior where the mixin(iq{
string s = i"foo $$(bar)";
});Will become: string s = i"foo $(bar)";Which becomes: string s = __d_interpolatedString("foo ", bar);So by default, any
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, double quoted strings have the same issue. Your example doesn't only apply to token strings: mixin("
string s = i\"foo $(bar)\";
");
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I think it is trivial with double-quoted strings because it is all syntax highlighted as a big string :) with token strings, the D lexer runs inside too so you might think it is different. But that said, your reasoning is solid and simple to understand, so I could go with that too. (just gotta be sure we mention all these things - and explicitly mention what we decide to exclude - to get past the Walter barrier :) ) And also I guess mixin should probably be able to handle the interpolated thing too. The PS this is a kinda weird place to be having this conversation, as comments on a random line of example code :P but whatever.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using enum SomeValue = 1234;
mixin(iq{enum AnotherValue = $(SomeValue + 1);});Seems wrong to me....I'll have to think about this one. |
||
|
|
||
|
|
||
| ## Description | ||
|
|
||
| Lexer Change: | ||
|
|
||
| Current: | ||
|
|
||
| ``` | ||
| Token: | ||
| ... | ||
| StringLiteral | ||
| ... | ||
| ``` | ||
| New: | ||
|
|
||
| ``` | ||
| Token: | ||
| ... | ||
| StringLiteral | ||
| i StringLiteral | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it makes sense to have an interpolated WysiwygString, it isn't WYSIWYG. You also can't have escapes so you can't have a
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Either way the DIP should specify what happens.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The string literal itself is still WYSIWYG. It's just that afterwards, it's processed by the interpolator. You can think of the interpolator like an operator and/or function call. For example, if you passed a WYSIWYG string to the "toUpper" function, the string wouldn't by WYSIWYG. |
||
| ... | ||
| ``` | ||
|
|
||
| No change to grammar. Implementation consists of a small change to `lex.d` to detect when string literals are prefixed with the `i` character. It adds a boolean flag to string literals to keep track of which ones are "interpolated". Then in the parse stage, if a string literal is marked as "interpolated", it lowers it to a sequence of strings and expressions. | ||
|
|
||
| Implementation and tests can be found here: https://github.com/dlang/dmd/pull/7988/files | ||
|
|
||
| ## Language Feature vs Library Feature | ||
|
|
||
| It has been brought up that this could be done as a library. Here is a breakdown of the pros and cons of a library implementation as opposed to a language implementation: | ||
|
|
||
| :white_check_mark: Library Pros: | ||
| - Requires no language changes | ||
|
|
||
| :x: Library Cons: | ||
| - Awkward syntax | ||
| - Bad performance | ||
| - Depends on a library for a trivial feature | ||
| - Cannot be used with betterC | ||
|
|
||
|
|
||
| :white_check_mark: Language Pros: | ||
| - High performance | ||
| - Nice syntax | ||
| - Better integration (IDEs, syntax highlighting, autocompletion) | ||
|
|
||
| :x: Language Cons: | ||
| - requires a language change | ||
|
|
||
| ## Breaking Changes and Deprecations | ||
| None :smile: | ||
|
|
||
| ## Copyright & License | ||
|
|
||
| Copyright (c) 2018 by the D Language Foundation | ||
|
|
||
| Licensed under [Creative Commons Zero 1.0](https://creativecommons.org/publicdomain/zero/1.0/legalcode.txt) | ||
|
|
||
| ## Reviews | ||
|
|
||
| The DIP Manager will supplement this section with a summary of each review stage | ||
| of the DIP process beyond the Draft Review. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better example would be to use a non-string variable: