If there's a constructor that looks like a copy constructor except that
it takes an rvalue instead of taking the argument by ref, then that type
can't have a copy constructor, and one of Tuple's constructors was
causing that problem. So, this fixes it so that it doesn't.
Stride is basically the same as std.meta's Stride aside from some
improvements to the tests and documentation.
Unique is the equivalent of std.meta's NoDuplicates except that it's been
updated in the same fashion as indexOf. So, instead of checking for
whether the elements are "the same" based on some complicated rules,
Unique takes a predicate which it uses to compare the elements for
equality (essentially making it the AliasSeq equivalent of
std.algorithm's uniq, though uniq requires that the range be sorted, and
Unique has no such requirement). So, the programmer can decide what kind
of comparison is used.
indexOf is phobos.sys.meta's version of std.meta's staticIndexOf.
The name was changed to better match our naming scheme. Because the
result of the template is a value and not a type or AliasSeq, it is
correct for it to be camelCased, but we don't normally prepend templates
with static, making it inconsistent to do so in the case of indexOf.
This might result in some symbol conflicts with indexOf from the
algorithm modules (assuming that we retain that function and its name
for Phobos v3), but a quick test showed that importing both
phobos.sys.meta and std.algorithm didn't result in a symbol conflict
when using the one from phobos.sys.meta. And even if we do get symbol
conflicts in some situations, the module system is designed to allow us
to deal with that.
As for the implementation, I've both made indexOf more flexible and more
straightforward.
staticIndexOf looks for an element in the AliasSeq which is "the same"
as the first argument, and that results in a big mess, since what "the
same" is varies considerably depending on what the elements are, and
staticIndexOf makes it even more complicated by evaluating some symbols
at CTFE if it can (e.g. evaluating a no-arg function that returns an int
to try to compare it to an integer literal) while not evaluating them in
other cases. Not only did trying to document the current behavior make
it clear that it's just way too confusing, but even trying to come up
with a sane simplification of how the comparison works was just too
messy, because it's trying to be able to compare just about anything
which you can stick in an AliasSeq. So, I punted on it by taking the
comparison out of the equation entirely.
indexOf now takes a template predicate. So, rather than looking for an
element which is the same, it looks for an element which matches the
predicate. This allows indexOf to be used in more cases than
staticIndexOf can be, _and_ it allows the programmer using it to decide
how the comparison works by choosing a predicate that matches what they
want.
So, in conjuction with that I added isSameSymbol and isSameType to
phobos.sys.traits, since those should correspond to the most common
searches that someone would be trying to do with staticIndexOf, but
since those traits are very specific in what they search for rather than
searching for an element which is "the same" in some nebulous sense, the
code should end up being much clearer and cleaner. And if someone wants
to do something completely different like indexOf!(isInteger, Types),
then they can, unlike with staticIndexOf.
They're the same as their std.meta counterparts, albeit with tweaked
documentation and examples.
I also fixed the casing on Map's parameters. They're supposed to be
types (or a template in the case of the template that it's applying to
the other arguments), so they should be PascalCased.
Both traits do exactly the same thing as their std.traits counterparts,
though these have far more documentation and examples.
I'm not a big fan of isImplicitlyConvertible, since it's usually a bad
idea to test for implicit conversions in template constraints, and the
is expression is incredibly simple to boot, but a trait _is_ needed when
a template predicate is needed (e.g. for the templates in
phobos.sys.meta), and having isImplicitlyConvertible allows for a good
place to put documentation on implicit conversions in general (as well
as to warn against testing for them in template constraints).
isQualifierConvertible isn't actually used much in Phobos v2 right now
(in most cases right now, Unqual is used with an is expression instead),
but it is used by the range traits, and there probably are a number of
cases where it would be better to use it than Unqualified or Unconst
with an is expression. But it's new enough that I don't think that many
of us have really thought through how best to use it yet. Either way, if
we want to be able to do something like isInputRange!(R, const char),
then it's a good trait to have.
They're similar to isSigned and isUnsigned from std.traits, but these
new traits are far more restrictive, because they're only true for the
four signed/unsigned built-in integer types, whereas isSigned and
isUnsigned accept a bunch of different types (including floating point
types and types that implicitly convert to real in the case of
isSigned).
From what I've looked at in Phobos v2, most uses of isSigned and
isUnsigned are used specifically when the code already knows that it's
operating on integer types anyway, so these traits should work for most
of those cases, and floating point types are always signed, so including
them in a check for signedness seems kind of redundant. I suppose that
isNumericSigned might make sense under some set of circumstances, since
it would allow for a single template instantiation instead of two like
you'd get with isSignedInteger!T || isFloatingPoint!T, but at least for
now, I think that isSignedInteger and isUnsignedInteger are enough.
As discussed in a recent planning meeting, we're renaming lib to phobos,
making it so that the root for Phobos v3 is phobos. So, that's what this
commit does.
However, on top of that, I removed the existing package.d files (both
lib/package.d and lib/sys/package.d) rather than fixing them, since I'm
inclined to think that we should be very selective about when we use
package.d, and I think that allowing something like import phobos; is a
mistake.
It wouldn't surprise me if we get screaming about that later, and maybe
we'll add it back at that point, but I'd rather not worry about
maintaining it as modules get added, and if we can get away with it, I
think that we should just not have it at all, since it's better practice
in general to limit the symbols that you import to something close to
what you're actually using, whereas import phobos; would be importing
everything. And having a package.d at each level like lib/sys/package.d
seems to be trying to do would complicate things even further.
We may very well end up with some package.d files in some cases, but I
think that that should be handled on a case-by-case basis - and that the
default should be to only have one module to import a symbol from.
So, with these changes, lib becomes phobos, and there are no longer any
package.d files in it.
The reason for this is because Unqual currently gets used heavily when
in many (most?) cases, Unconst should be used instead, because the code
in question is not supposed to be operating on shared, and thus
stripping off shared will either allow shared types in when they
shouldn't be (e.g. with a template constraint), or in the case of a
cast, it will actually strip off shared and the protections that it
provides, risking concurrency bugs. But a lot of D programmers are in
the habit of using Unqual rather than Unconst (probably in part because
Unqual has been around longer) when they simply want to remove const,
and by renaming it, we will hopefully discourage its use - or at least
make programmers think about it briefly rather than unthinkingly
grabbing Unqual.
Obviously, there are cases where Unqualified should still be used, but
hopefully, with the new name, it will be used less when it shouldn't be.
I've also adjusted the documentation on Unconst, Unqualified, and
Unshared to try to better clarify the situation with user-defined types
and member variables (since as Paul Backus has pointed out, it's often
incorrectly assumed that Unqual is guaranteed to strip const off of all
of the member variables when that's not actually true).
Actually organizing all of the symbols within a file is pretty much a
losing game in general, but I was trying to put the is* traits together
and accidentally put some in the section containing Unconst, Unshared,
and Unshared. So, this just moves Unconst and Unshared so that they're
next to Unqual again.
There is zero code that's actually changed. It's just moved within the
file.
isNumeric!T is equivalent to isInteger!T || isFloatingPoint!T, so I
suppose that its value is somewhat questionable with how simple it is,
but it does save a template instantiation in the process and arguably is
clearer for documentation purposes.
There is an isNumeric in std.traits, but I would have to study it in
detail to figure out exactly what it accepts, because it is written in a
rather confusing manner (though it does clearly accept enums, which this
isNumeric does not).
I have tried to make lib.sys.traits' isNumeric very clear in its
documentation about what it accepts.
isPointer does exactly what it says on the tin, so it's quite
straightforward all around.
I also beefed up the documentation on isDynamicArray to discuss a bit
about dynamic arrays vs slices and why the kind of memory that backs a
dynamic array is irrelevant to the type (and thus irrelevant to the
trait) - as well as pointing out that they're completely different from
pointers, if that isn't obvious enough.
isInteger is similar to std.traits.isIntegral, but I went with a
different name because it's specifically testing for the built-in
integer types and not for all types which are even vaguely integral. It
also helps distinguish from __traits(isIntegral, T) which accepts just
about anything and its grandmother which is even vaguely integer-like /
integral (to the point that I honestly question how much sense it makes
to ever use it).
The main difference between isInteger and std.traits.isIntegral is that
isIntegral accepts enums, whereas isInteger does not. isIntegral also
theoretically accepts cent and ucent, but since it's now an error to use
those, I don't think that it accepts them any longer in practice anyway.
isInteger makes no attempt to accept them and does not accept their
replacement types, because they're not built-in (and it's not clear to
me that we've fully sorted out what we're doing with those anyway).
isFloatingPoint is similar to std.traits' isFloatingPoint except that
the std.traits version accepts everything that __traits(isFloating, T)
does along with any type that implicitly converts to real. The
lib.sys.traits version accepts float, double, and real only.
Fix `value` missing reference.
Tweak wording.
Explain `pred` better.
Separate out needle overloads from the other 2.
Fix 'Returns'.
Split example into 2.
This ports over isSomeString and isSomeChar from std.traits.
isSomeString behaves the same (since the std.traits version doesn't
accept enums), but the new isSomeChar is different in that the old one
accepts enums, and this one rejects them. The new isSomeChar also has a
simplified implementation, but the behavior is the same aside from how
enums are treated.
The documentation and tests have been beefed up in the process, and I
added a bit more to the documentation / tests for isDynamicArray and
isStaticArray based on what came up with the tests for isSomeString and
isSomeChar.
It is not my plan to port over any of the other traits in std.traits
which are specifically for strings (e.g. isNarrowString), so checking
string types with lib.sys.traits should be less confusing than it has
been with std.traits.
Their behavior does differ slightly from that of their std.traits
counterparts, because they are false for enums even when an enum's base
type would match. std.traits is inconsistent with how it treats enums
(e.g. isSomeString is false for them, because it was causing bugs with
ranges for them to pass, but many of the other traits are true for
enums), but unfortunately, this isn't something that we've been able to
fix in std.traits (std.traits' isDynamicArray even mentions the problem
in a comment in its documentation), since it would break exising code
(in potentially very subtle ways no less).
I added a section to the module documentation explaining the reasoning
for how lib.sys.traits handles enums and implicit conversions with the
isXXX and hasXXX traits, which will hopefully not only make the
situation clear but also help educate some folks on the issues with
implicit conversions and template constraints.
I also beefed up the tests and documentation for isDynamicArray and
isStaticArray.
These do the same as their std.traits counterparts, but I've beefed up
the documentation and examples a bit.
I'm not sure that it's worth adding SharedConstOf, SharedInoutOf, or
SharedConstInoutOf as well, since that seems kind of excessive to me -
though it looks like std.variant does use SharedConstOf in its tests -
but in either case, I think that those can be left for a later commit if
we want to keep them, since this is already adding four symbols.
This ports std.meta.staticMap to lib.sys.meta as Map.
The implementation is the same, but the documentation and tests have
been expanded.
As has been discussed before, the way that std.meta names things is
inconsistent, and the pattern that lib.sys.meta is going with is that
its templates will be CamelCased like types, which matches what some of
std.meta does but not all of it.
A common cause of the "handler never matches" error is a template
handler that contains a typo or other compile-time error. Since the
location of the actual error is suppressed by __traits(compiles), users
often do not think to look for a mistake inside the handler itself. Now,
the message itself includes a hint to do so.
This starts lib.sys.traits with just Unconst, Unshared, and Unqual. Both
the tests and documentation have been beefed up from what std.traits
has, with many more examples provided to try to make the exact semantics
crystal clear, whereas the previous explanation in the docs was pretty
sparse. The actual semantics are the same though.
This isn't what I would have chosen for traits first, but they're used
heavily in unit tests, so they got moved up in the list.
I used version (StdDdoc) for the ddoc version, since that's what we do
normally in Phobos. I don't know if we'll want or need to change that
later given than Phobos v3 technically isn't Std, but all that really
matters there is that it's a specific version identifier that we use for
building Phobos' documentation and which no other projects would use.
And since Phobos v3 currently is set up to build with dub rather than
Phobos' normal build system, I expect that the documentation generation
is completely busted right now anyway. So, that'll need to get fixed at
some point.