January 12, 2024
On 1/12/24 16:27, Timon Gehr wrote:
> - Cannot support nested istrings. (I guess the `enum Format: string;` would mitigate this to some extent.)

- In any case, DIP1027 cannot support nested expression sequences without the user passing a manual marker. DIP1036e can support them quite naturally.
January 12, 2024

On Friday, 12 January 2024 at 06:06:52 UTC, Walter Bright wrote:

>

On 1/9/2024 3:49 PM, Paolo Invernizzi wrote:

>

CTFE capability is needed.

I concur that compile time errors are better than runtime errors. But in this case, there's a continuing cost to have them, cost to other far more common use cases for istrings. The cost is in terms of complexity, about needing to filter out all the extra marker templates, about reducing its utility as a tuple generator with the unexpected extra elements, larger object files, much longer mangled names, and so on.

The point is to pass the things that the compiler knows to the library, namely the string literal parts. Within the current domain of the D language, the best way to do this is to use string template parameters.

Necessarily, this is going to incur template symbol name explosion. I would love to solve this problem, especially in the cases where compile-time usage isn't needed. Having the compile-time expressions is essential when you need it, but is pretty ugly when you don't.

Again, we can have wrapper templates that do this for you. The problem (as always) is that these wrapper templates are still in there, still taking up space. Is there any room for a solution here? I'm talking about the compiler being clued in that these functions shouldn't exist in the binary. Then the compiler can take a lot of shortcuts (like hashing the type data instead of making a demangleable symbol).

But Timon is also right that the "format string" version is actually adding to the grief for library writers and users. There's no reason I can think of to add additional parsing requirements for the library. I'd prefer Jonathan Marler's solution of just interspersing strings and values if I had to pick between that and DIP1027. But that still leaves so much on the table of what could be great.

I also think it's fine to tell users 'Hey, you want formatted output? it's writef("format", args)'. My target was not and never will be, writef.

>

Want to know the source of my unease about it? Simple things should be simple. This isn't. The extra complexity is always there, even for the simple cases, and the simple cases are far and away the most common use cases.

It actually is simple. It's a simple transformation from a parsed expression to the subexpressions contained within (sprinkling in types to make it easy to know what is what). What you do with the transformation might not be simple, but that's not necessary to use the feature.

>

Frankly, it reminds me of C++ template expressions, which caught the C++ world by storm for about 2 years, before it faded away into oblivion and nobody talks about them anymore. Fortunately for C++, template expressions could be ignored, as they were not a core language feature. But DIP1036 is a core language feature, a feature we would be stuck with forever. And I'll be the one who gets the heat for it.

I just looked it up and... no. It's not even close. There is no requirement to make this complicated. The transformation is simple and straightforward. It's easy to understand if you take 5 minutes to read the docs.

If you want to build some insanely complex thing out of this, it's possible. But there is no requirement to use it that way. To reiterate, the feature is simple, what you can do with the feature is unbounded.

This is like saying templates are too complicated because of what you can do with templates.

>

P.S. You can do template expressions in D, too!

I rest my case ;)

-Steve

January 12, 2024
On 1/12/24 07:17, Walter Bright wrote:
> Please post an example of a problem it cannot detect.

For example:

```d
import std.stdio;
void main(){
    int x=2,y=3;
    writefln(i"%success: $y",x);
}
```
January 12, 2024
On 1/12/24 07:13, Walter Bright wrote:
> I'd like to see an example of how DIP1027 does not prevent an injection attack.

```d
// mock SQL
import std.format, std.variant;
class Sqlite{
    this(string){}
    Sqlite query(string command,scope Variant[int] args=null){
        writeln("EXECUTING");
        writeln(command);
        if(args.length){
            writeln("ARGS:");
            foreach(k,v;args){
                if(v!=Variant.init)
                    writefln(i"?$k = ($v)");
            }
        }
        writeln("DONE");
        return this;
    }
    struct Row{ int opIndex(int i){ return 0; } }
    int opApply(scope int delegate(Row) dg){
        writeln("ITERATING OVER ROWS");
        return 0;
    }
}
struct Statement{
    Sqlite db;
    string query;
    Variant[int] args;
    void bind(T)(int i,T arg){
        args[i]=Variant(arg);
    }
    void execute(){
        db.query(query,args);
    }
}

auto execi(Args...)(Sqlite db, Args args) {
    // sqlite lets you do ?1, ?2, etc

    string query = () { // note: parsing done at runtime
        string sql;
        int number;
        import std.conv;
        auto fmt = args[0];
        for (size_t i = 0; i < fmt.length; ++i)
        {
            char c = fmt[i];
            if (c == '%' && i + 1 < fmt.length && fmt[i + 1] == 's')
            {
                sql ~= "?" ~ to!string(++number);
                ++i;
            }
            else if (c == '%' && i + 1 < fmt.length && fmt[i + 1] == '%')
                ++i;  // skip escaped %
            else
                sql ~= c;
        }
        return sql;
    }();

    auto statement = Statement(db, query);
    int number;
    foreach(arg; args[1 .. args.length]) {
        statement.bind(++number, arg);
    }

    return statement.execute();
}

import std.stdio;

void main() {
    auto db = new Sqlite(":memory:");
    db.query("CREATE TABLE Students (id INTEGER, name TEXT)");

    // you might think this is sql injection... and you'd be right! the lib
    // cannot use rich metadata because it is not provided by the istring
    // therefore, it cannot verify that the user didn't construct the
    // query themselves in an unsafe way
    int id = 1;
    string name = "Robert'); DROP TABLE Students;--";
    db.execi(i"INSERT INTO sample VALUES ($(id), '$(name)')".format);

    foreach(row; db.query("SELECT * from sample"))
        writeln(row[0], ": ", row[1]);
}
```

Prints:
EXECUTING
CREATE TABLE Students (id INTEGER, name TEXT)
DONE
EXECUTING
INSERT INTO sample VALUES (1, 'Robert'); DROP TABLE Students;--')
DONE
EXECUTING
SELECT * from sample
DONE
ITERATING OVER ROWS


https://xkcd.com/327/
1 2 3 4 5 6 7 8
Next ›   Last »