Tuples, CTFE, and Sliding Template Arguments
Steven Schveighoffer
schveiguy at gmail.com
Sat Jan 13 17:12:14 UTC 2024
On Saturday, 13 January 2024 at 06:46:54 UTC, Walter Bright wrote:
> On 1/12/2024 8:35 PM, Steven Schveighoffer wrote:
>> On Saturday, 13 January 2024 at 02:16:06 UTC, Walter Bright
>> wrote:
>>> On 1/12/2024 4:15 PM, Steven Schveighoffer wrote:
>>>> I don't view this as simpler than DIP1036e or DIP1027 -- a
>>>> simple transformation is a simple transformation.
>>>
>>> Adding extra hidden templates isn't that simple. If a user is
>>> not using a canned version, he'd have to be pretty familiar
>>> with D to be able to write his own handler.
>>
>> Yes, that is intentional.
>
> So you agree it is not simpler :-)
No it is just as simple. I agree that the user should have to
understand the feature before hooking it. I meant it is
intentional that you can't "accidentally" hook istring calls
without understanding what you are doing.
And I don't understand this line of argument to begin with. You
have to be pretty familiar with D to hook anything:
* operator overloads
* foreach
* toString
* put
* DIP1027
* DIP1036e
* this thing you are proposing
And I'm sure there's more.
>
>> You should not be able to call functions with new syntax
>> because the parameters happen to match. We have a type system
>> for a reason.
>
> I proposed in the other topic to type the format string as
> Format (or FormatString), which resolves that issue, as a
> string is not implicitly convertible to a FormatString.
This doesn't help, as an enum implicitly converts to its base
type.
>
>
>>> 1027 is simpler in that if the generated tuple is examined,
>>> it matches just what one would have written using a format.
>>> Nothing much to learn there.
>>
>> In other words: "it matches just what one wouldn't have
>> written, unless one is calling `writef`".
>
> Yes, it is meant for writef, not writeln.
In none of the proposals I have written or supported, has it been
meant for `writef`. I don't understand the desire to hook
`writef` and `format`. The feature to make 1036e hook `writeln`
is just an easy added thing (just add a toString and it works),
but is not fundamentally necessary. We could just as easily
change writeln to handle whatever template types we create.
Hooking `writef` involves adding semantic requirements on the
library author that are specialized for `writef`, for what
benefit, I can't really say. You can always create a `writef`
overload that handles these things, but I don't see the point of
it. String interpolation isn't aimed at formatting, though it can
be used for it (as demonstrated).
>
>
>>> The other reasons:
>>>
>>> 1. preventing calls to functions passing an ordinary string
>>> as opposed to an istring tuple
>>
>> I don't see how this proposal fixes that. I'm assuming a
>> function like `void foo(string s, int x)` will match
>> `foo(i"something: $(1)")`
>
> Yes, we've seen that example. It's a bit contrived. I've sent a
> format string to a function unexpectedly now and then. The
> result is the format string gets printed. I see it, I fix it.
Me too. But shouldn't we prefer compiler errors? Shouldn't we use
the type system for what it is intended?
I've literally left bugs like this in code for years without
noticing until the actual thing (an exception) was printed, and
then it was hours to figure out what was happening.
> I can't see how it would be some disastrous problem. If it
> indeed a super problem, `Format` can be made to be a type that
> is not implicitly convertible to a string, but can have a
> string extracted from it with CTFE.
This would be a step up, but still doesn't fix the format
specifier problem.
> What it does fix is your other concern about sending a string
> to a function (like execi()) that expects a Format as its first
> argument.
Right, but this doesn't fix the format specifier problem. You
seem to be solving all the problems but that one.
>>> 2. preventing nested istrings
>>
>> Why do we want to prevent nested istrings? That's not a goal.
>
> I mentioned in another reply to you a simple solution.
Without a trailer, this isn't solvable technically. But I'm not
really concerned about nested istrings. I just meant that it
isn't a requirement to disallow them somehow.
>>> have already been addressed. The compile time thing was the
>>> only one left.
>>
>> A compile time format string still needs parsing. Why would we
>> want to throw away all the work the compiler already did?
>
> For the same reason writefln() exists in std.stdio, and people
> use it instead of writeln(). Also, the SQL example generates a
> format string.
The compiler is *required* to parse out the parameters. It has,
sitting in it's memory, the list of literals. Why would it
reconstruct a string, with an arbitrarily decided placeholder,
that you then have to deal with at runtime or CTFE? You are
adding unnecessary work for the user, for the benefit of hooking
`writef` -- a function *we control and can change to do whatever
we want*.
The SQL example *DOES NOT* generate a format string, I've told
you this multiple times. It generates a string with placeholders.
There is no formatting. In fact, the C function doesn't even
accept the parameters, those happen later after you generate the
prepared statement.
But also, SQL requires you do it this way. And the C libraries
being used require construction of a string (because that's the
API C has). An sql library such as mysql-native, which is fully
written in D, would not require building a string (and I intend
to do this if string interpolation ever happens).
Things other than SQL *do not require building a string*.
>
>
>> If you want to call `writef`, you can construct a format
>> string easily at compile time. Or just... call `writef` the
>> normal way.
>
> ??
Yeah, I've never cared about hooking `writef`, it's fine as-is
(well, it's fine if that's what you like). The fact that you have
to put in `%s` everywhere, it's a klunky mechanism for "output
this thing to a character stream".
Can't tell you how many times I've written a `toString` hook that
calls `outputRange.formattedWrite("%s", thing);`. That `"%s"` is
so ugly and useless. But this is all D gives me to use, so I use
it.
>
>> Compile-time string parsing is way more costly than
>> compile-time string concatenation.
>
> I suspect you routinely use CTFE for far, far more complex
> tasks. This is a rounding error.
Wait, so generating an extra template is a bridge too far, but
parsing a DSL at compile time is a rounding error?
In my testing, CTFE concatenation is twice as fast as parsing,
and uses 1/3 less memory.
Not to mention that concatenation is easy. I can do it in one
line (if I don't really care about performance). The same cannot
be said for parsing.
So I'd say, the user must understand that he's receiving a
template, but also does not have to learn how to properly parse a
specialized unrelated DSL. Format strings are weird, confusing,
klunky, and less efficient.
>
>
>> Ok. This does mean, for *intentional* overloading of a
>> function to accept a compile-time first parameter, you will
>> have to rename the function.
>
> You can overload it with existing functions, or give it a new
> name. Your choice, I don't see problem.
If the template-parameter version is less preferred, it will only
be used with an explicit template parameter.
It's not a problem, it just is one more quirk that is surprising.
-Steve
More information about the Digitalmars-d
mailing list