Proposal for scoped const contracts
Steven Schveighoffer
schveiguy at yahoo.com
Tue Mar 25 08:06:59 PDT 2008
"Janice Caron" wrote
> The more I think about this, the more I find I have to agree with
> Stephen completely. I think I might be able to come up with a better
> syntax though.
>
> Instead of
>
> out(T) min(T)(in(T) x, in(T) y)
> {
> return x < y ? x : y;
> }
>
> which is what Stephen suggested, I think this works better:
>
> inout(U=T) U min(T)(U x, U y)
> {
> return x < y ? x : y;
> }
>
> It's a pretty straightforward enhancement really. Just preceed a
> function definition with the attribute inout(U=T), and then within the
> function definition, all U's are in/out types. Here's the same idea
> used with strstr
>
> inout(U=char) U[] strstr(U[]s, const(char)[] pattern)
> {
> int n = s.find(pattern);
> return n == -1 ? null : s[n..$];
> }
>
> The reason I like this better is that it means we only have to define
> the "in/out" type once, instead of multiple times. Also - it means we
> don't have to ditch the existing uses of "in" and "out".
>
> We can also use it for member functions, except with a slightly modified
> syntax
>
> class String
> {
> inout(this) strstr(const(String) pattern)
> {
> /*...*/
> }
> }
>
> I like the idea of only having to express the inout type only once per
> function declaration, and I like the idea of not having to completely
> change the existing meanings of "in" and "out" in a way which would
> break old code.
>
> Another advantage of my syntax is that you could use inout(U=T) as an
> attribute to bracket multiple functions. e.g.
>
> inout(U=char)
> {
> U[] strchr(U[]s, char c);
> U[] strstr(U[]s, const(char)[] pattern);
> }
>
> Stephen, what do you think? Have I missed anything?
First, I'll say that I'm not a huge fan of my out() syntax. However, I do
like being able to tag exactly what is variably const on the arguments, and
I think it is necessary to tag the output, otherwise you lose expressiveness
and limit the syntax to certain types of functions.
So I'm glad that you are trying to find a better syntax, but I don't think
this is it. First, you are only allowing one type to be considered 'in' and
'out', where it must be different for properties (in that the return type
should be variably const based on the constancy of the 'this' pointer, but
the return type usually is not the same type as the 'this' pointer).
Second, I'd much rather have the 'char' in the argument declaration rather
than aliasing it to some other symbol ('U') at the beginning. I actually
think it's less clear the way you have it. Third, there is no way to
declare another variable of the same constancy as the input but of a
different type. In my scheme, in(x), means the type x that is somehow
derived from the input. So at any time you can declare any type and tag it
with 'in', which means it is based on the input (and therefore, should carry
the same constancy).
I have found there are more issues that my solution doesn't solve exactly,
so these need to be figured out.
For example, a linked list of mutable classes might be a template defined
as:
LinkList(T)
{
void append(T t) {...}
}
for the append function, the t argument is not modified during the function,
but the link list node added should be not const. So if a LinkList node is
Node(T), then how do you write the signature and body of this function? Is
it important to declare t as an 'in' parameter? I'm almost thinking that
there is no real way to have the compiler be able to prove that an argument
is not modified for all types of functions.
In addition, going back to the same example, append should only accept a
mutable object for t, otherwise, it cannot construct a proper node to add to
the list. So how is this information communicated to the compiler? If we
use:
void append(in(T) t);
Then a const or invariant t could be passed to the function, which would be
no good.
Is this even a concern that we should worry about?
-Steve
More information about the Digitalmars-d
mailing list