D Language Foundation March 2024 Monthly Meeting Summary
Mike Parker
aldacron at gmail.com
Wed Jul 3 17:06:50 UTC 2024
The D Language Foundation's monthly meeting for March 2024 was
held on Friday the 8th. It lasted about 90 minutes.
## The Attendees
The following people attended:
* Paul Backus
* Walter Bright
* Iain Buclaw
* Jonathan M. Davis
* Timon Gehr
* Martin Kinkelin
* Dennis Korpel
* Mathais Lang
* Átila Neves
* Razvan Nitu
* Mike Parker
* Robert Schadek
* Steven Schveighoffer
* Adam Wilson
## The Summary
### Item 1: ulong string literals and NUL termination
Martin said he'd been working on the new `ulong` string literals
for LDC and wanted to know if they should be null-terminated like
normal string literals. Walter asked what he meant, as string
literals were always secretly null-terminated.
Dennis asked if the same rule applied to hex string literals,
which were what Martin meant. Walter said no. Dennis said
internally, it was possible to have hex strings or other arrays
being a string expression. The compiler would still allocate a
null byte in the data segment even if it were an integer array.
Walter said hex string literals were converted to binary data by
the compiler, and asked why you'd want the null byte. Dennis said
it wasn't that you'd want it, it was that the compiler was just
adding a zero terminator in code generation when it saw a string
expression. A string expression didn't have to be a string
literal. It could be an array, an import statement, or a hex
string literal. Internally, there were multiple ways to create a
string expression that wasn't a string literal.
Walter said the implicit null should only apply to actual string
literals. Mathias asked if there was a downside to doing it in
those other places. Because it was put after the array, it only
mattered if you took the pointer. Steve said that was correct. He
thought the test should be, "Does the compiler explicitly cast
this to a character pointer", in which case it should add the
terminator.
Paul said he was pretty sure there was code that relied on the
current behavior of the zero terminator being applied to strings
generated via CTFE, so he would be wary of changing it. Walter
agreed but said those were creating string literals, so the
terminator should always be applied anyway.
Dennis said he wasn't sure about that. He thought that sometimes
you could create a string from a character array that was still a
character array internally. He wasn't sure if the compiler added
the terminator in that case.
Martin said there were two special cases he was doing in
implementing the new 64-bit hex string literals in LDC. The first
one was that he was not zero-terminating them. The second one was
that he wasn't going to cache them. All other string literals
were mergeable across object files. He thought that because these
hex strings were intended to represent binary data, you wouldn't
be interested in the null terminator and he assumed they'd be
larger than the average string literal. He'd had a quick look at
the DMD PR and it appeared as if there were no special cases at
all. So in DMD, they were likely still zero-terminated and merged
across object files like other string literals.
Walter asked why you wouldn't want it merged if it was going into
static data. Martin said he assumed the test would be expensive
because he used the string itself as the caching key. He had no
idea about the new Unicode tables in Phobos where this feature
was used, but he assumed they could be pretty large, like a
couple of megabytes or so. In that case, the check would be
expensive. He assumed that it would be unlikely that you'd have
duplicates of these hex strings across object files.
Walter said that made sense. Martin said his main point was he'd
expected a spec change, but that wasn't even mentioned in the
changelog. It mentioned that you could cast any string literal to
a ulong literal, but the compiler had changed in that the string
expression could now have a character size of 8 bytes.
Walter asked if Dennis could take care of that. Dennis said he
would.
### Item 2: Deprecating range API functions on Nullable
Dennis linked to [a PR that Jonathan had
submitted](https://github.com/dlang/phobos/pull/8921) but had
since closed. The PR deprecated treating `Nullable` as a range.
He wanted to know our opinions about it.
Adam said he and Jonathan had had a long discussion about this
the night before. He said he used nullables a lot in C# and loved
them. But C# had special operators to use with `System.Nullable`.
People hadn't really use `Nullable` in C# before those operators
came in. For example, when you had a `Nullable` and it was
`null`, the question then was what should the default value be?
And there was an operator to use for that when assigning it. So
he had wondered if we could do this in D, and Jonathan had given
him a list of reasons of why that wouldn't work in D.
Then they'd had a long discussion about `.init`. Adam had brought
C#'s non-nullable classes and wondered if we could do those in D,
but Jonathan had pointed out all the problems we'd have with
`.init` if D had non-nullable classes. For example, if you went
into a dynamic array with a non-nullable class, what would it do?
It would be like the problems we had when we implemented the
disabling of destructors. It would be like, it wasn't an
initialized struct, but a dynamic array needed an initialized
memory hole.
He said our `Nullable` wasn't actually a nullable yet. Maybe it
was an optional, or just a type, and we got the name wrong.
Because we weren't doing the things with it that a nullable could
do. So should `Nullable` be a range? He didn't think so. A
nullable was just saying that this data could have a null value
attached to it without the data itself being null.
Paul agreed that our `Nullable` wasn't analogous to that of C#
and that it was really an optional, or a type with a misleading
name. He thought that in Phobos v3 we should rename it and add an
actual C#-style nullable, but that was beside the point for this
discussion. As for Phobos v2, he felt that trying to use
comparisons to the C# nullable as a guiding principle for its
design was of limited utility.
As for whether `Nullable` should be a range, he was split. He
could see merits on both sides. But there was a reasonable case
to be made on the grounds of separation of concerns that we
shouldn't have types be both ranges and other things. He'd be
okay if we wanted to adopt that as a design guideline for Phobos
v3. But it wasn't obvious to him that it was a big enough deal to
justify breaking Phobos v2 given how many other problems in v2
we'd elected to put off fixing.
Átila said he agreed with maybe everything Paul had said. He said
Jonathan had talked to him about this before, and he was also
sort of torn because `Nullable` in Phobos was like an optional
type, which was basically a container with zero or one element.
So why shouldn't it be a range? That looked like a range to him.
He said that maybe we should distinguish between containers and
the ranges they made available. Because you could consume a
range. Did that also mean consuming the container? Maybe it would
be a good idea going forward that Phobos v3 containers not be
ranges, but to return a range.
Timon said he had wanted to make a similar point. Clearly,
`Nullable` or `Optional` or whatever was a container, and the
range interface mutated the range if you popped it. We wouldn't
expect to change the underlying static array of a dynamic array
when `popFront` was called. That was what this was essentially
doing. So it shouldn't be a range. He didn't know if it was worth
a breaking change, but he did believe that making it a range had
been a mistake.
Robert said that he was pretty sure that among all of us, he had
the most nullable code in production. When he first saw this PR
and realized that `Nullable` had a range interface, it had
terrified him quite a bit.
He said there was a wider point: we needed to be less clever. D
was too complicated. We were trying to push in too much type
theory. He would like the first edition we implemented to be
really, really boring. If somebody wanted to do magic... just no.
He got a thumbs up from Adam, Walter, and Átila.
Átila said there was also *too* boring. We didn't want to go full
Go. Robert said we should be full Go with templates. He said Go
was an awesome language. It was boring, but boring was cool. The
vision statement we'd come up with wasn't about writing cool
software. We wanted to enable stuff. He said the M8 wrench he
owned and the hundreds of M8 bolts he had lying around were
boring, but they always worked and the wrench was always the
same. Maybe an M10 or an M2 bolt might be nicer, but the M8 was
awesome and boring.
Átila said we should make things as boring as possible, but no
more boring than that.
Walter brought up strings in Phobos. There were all kinds of
templates to check if something was sort of a string or kind of
behaved like a string or was a string literal or a string made
out of ints, and so on. The way Phobos was now, it was impossible
to tell what a template accepted in the form of a string. He
thought that was very bad.
Another thing he didn't like about Phobos, and thought this might
be a language design issue, too, was having something that
accepted both a range and an array. Arrays should be more like
ranges and not have this distinction between them. He knew you
could import `std.array` and get the range API for arrays, but
he'd been thinking that should be revisited. Maybe the language
should be modified to allow `empty` and `popFront` to be called
on arrays.
Átila said that was easy: just put the array range API in
`object.d`. Walter said he hadn't thought too far along it yet,
but he wanted D arrays to implicitly be ranges and not have to
explicitly say they were ranges. He thought it would make things
simpler to do that. Átila agreed.
Along those lines, Walter had submitted several pull requests to
simplify DMD internally. He was trying to follow the rules he'd
laid out [in his DConf '23
presentation](https://youtu.be/3iWn4S8JV8g). It should be so
simple that everybody thought that anybody could have written it.
That was a large goal he had with the DMD codebase: look at a
piece of it, figure out what it was doing, and rewrite it so that
it was stupidly simple.
Coming back to `Nullable`, Robert said if there was a push to
remove the range interface, then we should also remove the
no-argument form of `get`. That sometims tripped him up. He'd
call it without an argument, it would blow up in his face, and he
would feel stupid. If it weren't there, he wouldn't feel stupid,
and that would be cool.
Walter said if you accidentally did it regularly, the interface
was probably wrong. Robert said it was easy to do wrong. Walter
said that default arguments should be used very rarely.
Paul said that for the rare cases when you were writing some kind
of low-level code or when you'd already done the check
separately, there should be a way to access the value of a
nullable without checking. But it should probably be named
something like `uncheckedGet` or `unsafeGet` instead of just
`get`.
Átila agreed, and proffered `tryGet`. He added it should probably
be made `@system`. Steve thought `tryGet` sounded correct. Timon
proposed `getOrDie`. Robert said this kind of thing had to hurt
to type out. It had to be clear that this was "get with a foot
gun". Átila offered `prettyPleaseGet`.
### Addenum: Phobos v3
Dennis knew that Walter didn't like `isSomeString` or
`isSomeChar`, but said [they had made it into Phobos
v3](https://github.com/dlang/phobos/pull/8940). Walter joked that
we should kill it before it spawns. Jonathan said it was very
much necessary.
Adam said he'd talked with Jonathan at length. They'd been slowly
porting traits over together. `isSomeString` and `isSomeChar`
were the only ones going in for `string` and `char`. He said
they'd talked about `isString` and `isChar`, but he left it for
Jonathan to explain his reasoning.
Jonathan said that `isSomeString` told us that something was a
dynamic array with an element type of `char`, `wchar`, or
`dchar`, and `isSomeChar` did the same for characters. They did
exactly what you were asking for so that templates that needed to
operate on those things could check. Stuff like
`isAutoDecodableString`, `isNarrowString`, or anything like that,
was completely unnecessary and wasn't making it into v3.
Walter asked about a struct that had an `alias this` pointing at
a string. Jonathan said that was an absolute "no". No enums. No
implicit conversions. Only things that gave you exactly what you
were asking for so that it was clear and you did't end up
allowing an implicit conversion or any related bug in your code.
You had to explicitly say you wanted the conversion. None of the
traits they'd ported over so far passed for `enum` or `alias
this`. They all gave you exactly what you were asking for from a
list of types.
Walter said `isSomeString` was already wrong, because a string
literal or a slice was something different from a string. As a
user, he had no clue what `isSomeString` included. Calling
something a string when it was actually a mutable array of `char`
was pedantically incorrect, as a string was immutable. Jonathan
said that with the general terminology we'd been using, it would
tell you if something was a string or an array of characters.
Walter said he agreed, but it was wrong.
Jonathan said the "some" in the name told you it was not
specifically `string`. Walter said his thoughts on this weren't
fully fleshed out yet, but he thought the convention should be
that "string" referred to an immutable array of `char`. Anything
checking for an array of `char` should be called "chars", or
"charz" if zero-termination was expected. He said that made
sense. He thought we should stop combining "string" and "array of
characters" in Phobos.
Jonathan countered that most string processing didn't care if the
array was mutable or immutable. Walter agreed, but insisted that
a mutable array of `char` was quite different from a `string`.
Átila said the only reason he could see this type of trait even
existing was when you didn't care and all you wanted was a range
of `const(char)`. Other than that, "string" should mean `string`
and all the other things should just disappear. If you wanted to
deal with UTF16 or 32, then convert.
Walter said he thought that was something different. He said he'd
talked with Adam and they had agreed that character array
manipulation in Phobos should only operate on `char`. But that
was something different. He just wasn't sure we wanted to call
arrays of mutable characters "string" in Phobos.
Átila agreed but said a lot of algorithms won't care and should
be able to take an array of mutable, immutable, or constant.
Jonathan reiterated that a lot of string processing just doesn't
care. Walter said it should be clear from a function's name if
it's planning on mutating or if it doesn't care. Átila said his
point was that he could see a need for a trait because of this,
but he couldn't see any other need for one.
Timon said he'd been wondering if we even needed this and what
the actual use case was. He supposed it was what Átila had said:
when you wanted to write an algorithm that worked specifically
for strings, and the first thing it did was turn it into a range
of some sort of character. But did we need that? Couldn't the
algorithm just accept the range in the first place?
Paul said that if we did need this for something, then perhaps
the thing to do would be to keep the semantics the same but
change the name to something like `isSomeCharArray`, which was
more descriptive of what it actually did. Átila didn't think we
needed it. We had a constraint right now where we could test
`if(isInputRange!(r, char))`.
Robert said that Phobos v3 should not use template constraints
for overloading, because in v2 right now that was hell. There
should just be one function. And if we needed different
implementations, that one function could be the jump-off point.
Otherwise, the error messages were just going to be crap.
Átila said doing the checks inside the function instead of via
constraints wouldn't be better. Robert said it would be better
because you'd write your `static asserts` with a message saying
why that combination of parameters couldn't be passed to that
function. The error messages would be a lot better.
Timon said that may be better pragmatically, but from the point
of view of what it meant, it was worse. It would be saying that
you could call this Phobos function, but it wouldn't compile. By
default, that would be assumed to be an internal error. So maybe
what we should strive for would be to make the pragmatics match
the ideology somehow. Like, fixing the language so that we could
get the pragmatics while doing it the right way.
Jonathan said that for several functions if you were looking to
overload, you could have a simplified template constraint at the
top level and do more complex stuff internally for the overloads.
Then it would be easier to see what was going on. The problem was
when we had extra overloads with different numbers of arguments
and such to allow various behaviors. Once you did that, you
couldn't stick the checks inside, which was why we usually
didn't. There were probably places where we shouldn't have done
that, and there were probably places where we didn't do that, but
where we still overloaded and had constraints when we didn't need
to have.
He didn't know how fixable this was in the general case, but he
agreed it was something we needed to look out for to simplify
what was there so that we didn't have to look at as much template
constraint soup. We should probably look at using different names
in some cases. We did have some issues where we overloaded on
element vs. range, and that had caused some bugs in some cases.
Like output ranges accepted both elements and ranges for `put`,
and he'd run into bugs when, for example, trying to put a range
in an output range and it went in as an element.
He didn't know if we never wanted functions that accepted both
ranges and elements, but we should be thinking about the effects
of that. Should we be overloading or using different names? For
example, should `put` instead be `putOne` for elements and
`putMany` for ranges? That sort of thing.
Walter agreed and said that one way to clear up overload
confusion, not only for the user but for the code itself, was to
use different names for different purposes. Like appending an
element. In the language itself, appending an element and
appending an array was overloaded. It depends on the type. That
may have been a mistake.
Jonathan said it was also simplified in the language because you
always knew you were dealing with an array and not a whole range
of things that could match. Walter agreed but said overloads were
something that should be used as sparingly as possible. In
Phobos, we'd used them eagerly. Too often he couldn't figure out
which overload was being instantiated.
Robert said this all made sense from the standpoint of
theoretical design and language design, but he saw D as an
engineering tool. It was a wrench for an M8 bolt, and we were
trying to design a better bolt. He said no, it had to be stupid.
Having distinct names was as stupid as we could get, and he liked
that.
Timon said he thought it was a very good idea to reduce the
number of overloads. One reason that hadn't been mentioned was
compile times. When you had a function with a lot of overloads
and a lot of template constraints, every time you called it, some
of them would get pruned, but for many overloads, you would then
instantiate all the templates and all the constraints to check
them. That wouldn't be necessary if you just said from the start
what you wanted because then everything would just be a hash
table lookup in the compiler, which was immeasurably faster. It
also made the code easier to read because you wouldn't need to
expand all those constraints.
On a side note, he added that he'd copied D's syntax for array
concatenation in his language, but he hadn't included the support
for appending single elements.
Átila said he thought Timon was right. We should do things right
and get better error messages. Maybe we needed facilities in the
language to be able to write easier signatures. For instance, it
was never the case that someone would forget to implement a
virtual function and couldn't figure it out, because the compiler
would tell them that they didn't implement it. We didn't have
that for compile-time interfaces, and he thought that was what
was lacking here.
Robert said that sounded too much like C++ concepts and he didn't
want that. Dennis mentioned Rust traits. Átila brought up Haskell
type classes. He said that if you wanted to declare that a given
type satisfied a particular compile-time interface, then you
should be able to get compilation error messages that tell you
the reason why it failed.
Timon thought a lot of that would just be better compiler
engineering. He agreed that we should be wary of making the type
system too complicated, but he thought allowing an error message
on a constraint would go a long way.
Paul said that the way templates were type-checked in D was
similar to the way code was type-checked in dynamic languages
like Python, Javascript, Ruby, and so on. He thought the
consensus among programmers in those languages for handling
issues arising from type checking was to adopt a strong unit
testing discipline. He thought that was the best path forward for
D.
He said there was a fork in the road in language design where you
could choose to go the template/macro direction or the
traits/type classes/signatures direction. D had more or less
fully committed to templates at this point. Trying to change our
minds on that would add a huge amount of complexity for
relatively little gain. But he did think there was a lot we could
do to help programmers write better unit tests for their
templates, and what people were doing in dynamic languages
suggested that would pay off.
Jonathan said that testing was something you had to do quite
thoroughly with templates if you wanted things to work. This came
up with ranges quite a lot and didn't get done anywhere near
enough, in part because the range API allowed too much. There
were too many variations. Part of that was autodecoding, and part
of it was the reference type stuff that was allowed. He thought
that for some of these cases, we should be more strict about what
we were dealing with as opposed to letting everything under the
sun go through. That would make it easier to write tests and make
sure things worked properly.
He also thought that we should be providing testing facilities
for some things, ranges in particular, in the form of test types
that could be used with user code, if possible. Types that users
could employ to test that their code worked with various ranges
or behaved properly in various circumstances. We couldn't cover
everything, but we should do as much as we could. Non-copyable
types, for example. Most people didn't test for those because
they just didn't think about it. Having testing facilities
available to test for them would make a difference.
Martin said he'd just remembered something regarding Phobos
templates he would like to see changed in v3: how predicates were
handled. Take `filter` for example. The predicate there was an
alias function. For every lambda you gave it, you ended up with a
new instantiation of the template. He'd like to see an overload
in Phobos v3 where the predicate wasn't an alias, but a delegate
type. A templated one to account for attribute inference.
He added that not only would this be good for compile times, but
it would also help eliminate an error that newbies encounter with
`map` and `filter` needing the instantiation operator, which
wasn't necessary for other languages. We needed a compiler
improvement to make it work. It was doable already, but it
required an explicit delegate signature. Ideally, the compiler
would work that out for you. [He'd opened an issue about
it](https://issues.dlang.org/show_bug.cgi?id=22501).
Paul linked to [a Phobos
v3](https://github.com/LightBender/PhobosV3-Design/discussions/20) discussion about taking predicates as runtime parameters. Steve noted there was a link there to [a forum post from Vladimir Panteleev](https://forum.dlang.org/post/qnigarkuxxnqwdernhzv@forum.dlang.org) that made a pretty strong case for why we'd want this over alias parameters. He encouraged everyone to read it.
Adam took us back to `isSomeString` and asked if Walter was okay
with what we'd discussed. Walter said we should at least consider
changing the name, but we should seriously reconsider if we
needed it. He reiterated that with something like that, he didn't
have a clue what it was actually accepting. Átila said he still
wasn't sure if we needed it.
Adam said he and Jonathan would have to have more discussion
about it, as Jonathan knew more about it than he did. He also
said it sounded like he needed to add a section to the Phobos v3
design document saying to avoid overloads in function names where
possible.
Walter said it should be something you use rarely, not because
you couldn't think of another name for something. He thought
overloads were great for something like `std.write`, but for a
lot of cases, it seemed to him that they were used because
somebody wanted to avoid coming up with a proper name.
Another thing he'd come to dislike was default arguments. Those
should be pretty rare. Adam said he'd add a section to the
document about default arguments. Átila said he'd be wary about
adding rules like that for API design, as it really depended on a
case-by-case basis.
Timon noted that there were times when you might realize that a
function needed an extra argument for something you needed to do,
so you'd add a parameter with a default argument rather than
going back to fix all the call sites or refactoring. It was for
one specific use case. He'd seen that happen in codebases he'd
worked on where people added 15 default args to a function. If
you were thinking of the API up front, it was less of a concern.
Robert suggested the thing to do in that case was to pass a
struct. You'd add the arguments you needed as fields and pass it
in. Then when you needed to add something else, you added it. He
said that had worked well for him. He thought we all could agree
that if you had 15 default arguments for a function, we'd be
having a stern talk with you. This prompted a side discussion
about what the max number of args for a function should be and
how many a programmer could keep in memory.
Átila then took us back to the Phobos document, saying it was
better to have more general guidelines like "avoid allocations"
or "avoid exceptions" rather than specifics about what the API
should look like. Too much taste goes into that.
Adam said that he came from a very checklist-driven background
with his flying experience. We know what the policies are in many
cases, and the checklists serve to remind us to check for these
things. In our case, the checklists were for the reviewer to go
through and say, "Is there something we should be doing to reduce
the number of overloads and default arguments?" It wasn't to say
that you could never use them, just to make sure you checked to
see if there wasn't a better alternative.
Adam said he'd started tagging any Phobos v2 PR with a label that
indicated the submitter should check it against Phobos v3 to see
if it was something that should be copied over. We were going to
keep v2 around for a while, so this was something we always
needed to check for.
Steve said that once v3 was released, we shouldn't be allowing
PRs to v2 anymore other than bug fixes. Adam said he and Walter
had discussed that, yes, and also regression fixes when things
stopped building. He said Walter had been very clear that v2 must
continue to function.
Adam said there had been discussions in the community about v3
that went beyond what we were planning, and we should probably
tighten up the language we use in talking about it. For example,
he'd had conversations with people who thought we should keep
adding new features to v2, or that we should use v3 as a complete
redesign of Phobos. He said he wanted to completely redesign
traits by getting rid of them.
Jonathan said we'd then be screwed for templates in general. We
needed some of the traits, some weren't necessary, and some just
needed to be reworked. We could debate which were which, but
without traits at all, or some sort of language change that
enabled another solution, everyone would have to implement the
same stuff manually anyway. That would be a mess.
Walter said that some of the descriptions for what some of the
traits were testing for were incomprehensible. Jonathan said that
as he was porting them over to v3, he was being very explicit
about the documentation. Not only in the descriptions but also in
a lot of examples showing exactly what they did.
As an example, the v2 standard traits were very inconsistent
about accepting enums. Some did and some didn't. He thought it
was bad practice for them to accept enums in general, as that was
an implicit conversion that didn't take place unless you forced
it. So you needed to be testing for it and then doing it
explicitly so that you didn't do it accidentally. But, in porting
things over, he was looking at the implementations to see what
they were doing and clearly documenting whether they accepted
enums or not.
Jonathan said that clarity of the trait names was always going to
be an issue. A name could only get across so much information and
could be misinterpreted. So we needed to make sure we had solid
trait names. That was another reason to be as explicit as
possible about the documentation.
Walter thanked Jonathan and said he was just triggered by
`isSomeString`. Jonathan said we needed that one in Phobos v2
because of autodecoding and whatnot. We wouldn't need it in v3
anywhere near as much. We wouldn't be doing so much overloading
on strings because we were going to focus on arrays of `char`
rather than `dchar`. Whether we kept it in v3 or not, it wouldn't
be necessary for Phobos.
Timon noted that some traits, like `hasDestructor`, had to accept
enums. Jonathan agreed, but in cases like that, the trait was
testing only on the specific type it was given, `isSomething` or
`hasSomething`, not on what the type could be converted to. So
for those cases, the documentation had to be very clear that this
trait accepted enums and why.
Paul added that it wasn't just the Phobos traits that had the
problem with implicit conversion of enums, but also the built-in
`__traits`, like `isIntegral` and `isFloatingPoint`. He thought
that should be tightened up in a future edition of the language.
Steve said if we were simplifying traits, we should look at
getting rid of things that were just wrappers for simple things.
For example, we could drop `isDynamicArray` because we had an
`is` expression to do the same thing. We could also get rid of
things that had been superseded by built-in traits and were now
just a shell calling out to the built-in.
Jonathan said there were definitely some that he wasn't porting
over. Like `isBool`. This one tested whether something was a
`bool` and whether it was an `enum`. There was no point in
porting that over, as all you needed to do was to check if
something was a `bool` using an `is` expression. But in other
cases, having the named trait was going to be a lot simpler for
folks to remember. `isDynamicArray` was more obvious and
memorable than the alternative `is` expression that did the same
thing.
He said he'd been going through and porting traits over one by
one as necessary for the tests. At the end, there would be a list
of things he didn't port because he decided it didn't make sense
to port them. We could then go through and decide if they needed
to go in or not. And in some cases, like `isSomeString`, if
everyone decided, "we don't like that", then we could rip it out.
He was keeping the PRs small so they were easy to review for
anyone who wanted to look at them.
Steve said that minimal was better. Once a trait was in there, it
was in there and we'd be stuck with it. He said that regarding
the simple traits like those replaceable with an `is` expression,
Dennis had brought up a good point in the chat that you couldn't
pass an `is` expression or a `__traits` call to a template. That
was another thing to think about.
Razvan noted that he'd asked Andrei a few years ago about those
simple kinds of traits. Andrei had told him that people shouldn't
be calling `__traits` directly, even in cases like `isConst` or
`isImmutable`, because it made the code look nicer. Razvan
brought that up because he thought that was the reason they'd
been added, but he didn't agree with it.
Walter said that actually predated Andrei. It had come about
because he couldn't think of a good syntax for traits. So he'd
settled on something deliberately ugly, `__traits`. The idea was
that you'd then put a pretty face on it in Phobos. Maybe the
thought had outlived its usefulness because people seemed to like
them. That was something we could certainly revisit in the Phobos
redesign.
Dennis said maybe people preferred `__traits` because they didn't
like importing `std.traits` to use them. Walter said another nice
difference was that you weren't adding overhead with the
built-ins. The Phobos traits, as templates, came with overhead.
Walter said another thing he'd wanted to bring up with Jonathan
was the nomenclature of "dynamic arrays" vs. "slices". Back when
he'd added dynamic arrays to D, he hadn't been familiar with the
usage of "slice". He hadn't seen it used much. These days, you
saw it pretty often out in the broader programming community. We
should consider our usage of the terms. People now would know
what they were, except when they expected a dynamic array that
was growable and shrinkable. So we needed to be consistent in our
terminology: if it was expected to be appended to, it was
growable and shrinkable, then it was a dynamic array; otherwise
it was a slice.
Jonathan said the problem was that there was no difference as far
as the types went. Walter said that was correct. It was too bad
we couldn't encode that somehow in the type, but we could at
least signal the difference in the nomenclature. Martin agreed.
In his mind, a dynamic array had always been a slice backed by
the GC.
Regarding the overhead of templates vs. `__traits`, Martin
brought up an issue they'd encountered in the Symmetry codebase a
couple of years ago. The `Unqual` template was getting
instantiated something like a million times. Lots and lots of
duplicates. Consider that each instantiation was 100 bytes,
probably more, but for the sake of example, consider a million
instantiations x 100 bytes. It wasn't just `Unqual`, but other
stuff as well. The solution was to replace `Unqual` with an `is`
expression.
Unfortunately, syntax-wise that was much longer than just using
`Unqual`. So maybe there was a way we could treat some of these
`std.traits` templates as built-ins. They didn't have to live in
Phobos. They could live in DRuntime.
Walter said the way to do that was to have the compiler recognize
the template and not instantiate it, but just return the result.
He'd done that for a couple of the templates. `Unqual` sounded
like a good candidate for that. Then it would still look like a
template, but it wouldn't have the cost of a template anymore.
Razvan said that there was always this thought that if something
was in Phobos and was being used in Phobos, then we needed to
keep it. But we didn't need to consider that when deciding which
Phobos traits to keep. We could always just use `__traits` and
`is` internally. But for the users, there was always a compromise
between convenience and performance. Many users might prefer the
convenience of the standard traits. So we could use `__traits`
internally but still offer the standard interface.
Jonathan reiterated that for now, he was only porting over the
things that Phobos needed for the tests. When he was done with
that, then we could decide to replace them internally with
`__traits` or `is` expressions, and decide whether or not to keep
them in the API and which of the remaining ones to port over.
Razvan suggested that another way to look at it was that if some
`__traits` were useful, then we should consider prettifying them
and making some extra syntax for them to become first-class
citizens. If it was a feature, it was a feature. He didn't see a
point in encouraging people to use wrappers for them.
Átila said that Razvan's comments made him think that a good
guideline for Phobos v3 would be to minimize interdependencies.
Adam agreed. He said he'd add something like that.
## Conclusion
To wrap things up, I asked everyone if I should schedule a Phobos
v3 planning session. Everyone said yes. We held that on March
16th. This was followed by a quarterly meeting on April 5th and
our next monthly on April 12th.
And now the usual reminder: if you have anything you'd like to
bring to us for discussion, please let me know. I'll get you into
the earliest monthly meeting or planning session we can arrange.
It's always better if you can attend yourself to participate in
the discussion of your topic, but if that's not possible, I can
still put the item on the agenda without your presence with a bit
of preparation.
More information about the Digitalmars-d-announce
mailing list