Checking function parameters in Phobos
Meta
jared771 at gmail.com
Sun Nov 24 23:24:03 PST 2013
On Sunday, 24 November 2013 at 17:35:51 UTC, Simen Kjærås wrote:
>> I believe inout's point was this, though:
>>
>> Validated!(isPositive, lessThan42, int) i = foo();
>>
>> Validated!(isPositive, int) n = i; // Fails.
>> Validated!(lessThan42, isPositive, int) r = i; // Fails.
>>
>> This is of course less than optimal.
>>
>> If a type such as Validate is to be added to Phobos, these
>> problems need
>> to be fixed first.
>>
>>
>>> Or just pass a function that validates that the int is both
>>> positive and
>>> less than 42, which would be much simpler.
>
> I've created a version of Validated now that takes 1 or more
> constraints, and where a type whose constraints are a superset
> of
> another's, is implicitly convertible to that. Sadly, because of
> D's lack
> of certain implicit conversions, there are limits.
>
> Attached is source (validation.d), and some utility functions
> that are
> necessary for it to compile (utils.d).
>
> Is this worth working more on? Should it be in Phobos? Other
> critique?
>
> Oh, sorry about those stupid questions, we have a term for that:
>
> Detroy!
Awesome, I was messing around with something similar but you beat
me to the punch. A couple things:
- The function validated would probably be better named validate,
since it actually performs validation and returns a validated
type. The struct's name is fine.
- I think it'd be better to change "static if
(is(typeof(fn(value)) == bool))" to "static if
(is(typeof(fn(value)) : bool))", which rather than checking that
the return type is exactly bool, it only checks that it's
implicitly convertible to bool, AKA "truthy".
- It might be a good idea to have a version(AlwaysValidate) block
in assumeValidated for people who don't care about code speed and
want maximum safety, that would always run the validation
functions. Also, it might be a good idea to mark assumeValidated
@system, because it blatantly breaks the underlying assumptions
being made in the first place. Code that wants to be rock-solid
@safe will be restricted to using only validate. Or maybe that's
going too far.
- Validated doesn't work very well with reference types. The
following fails:
class CouldBeNull
{
}
bool notNull(T)(T t)
if (is(T == class))
{
return t !is null;
}
//Error: cannot implicitly convert expression (this._value) of
type inout(CouldBeNull) to f505.CouldBeNull
void takesNonNull(Validated!(CouldBeNull, notNull) validatedT)
{
}
- On the subject of reference types, I don't think Validated
handles them quite correctly. This is a problem I ran into, and
it's not an easy one. Assume for a second that there's a class
FourtyTwo that *does* work with Validated:
class FortyTwo
{
int i = 42;
}
bool containsFortyTwo(FortyTwo ft)
{
return ft.i == 42;
}
void mutateFortyTwo(Validated!(FortyTwo, containsFortyTwo)
fortyTwo)
{
fortyTwo.i = 43;
}
auto a = validated!containsFortyTwo(new FortyTwo());
auto b = a;
//Passes
assert(a.i == 42);
assert(b.i == 42);
mutateFortyTwo(a);
//Fails
assert(a.i == 43);
assert(b.i == 43);
This is an extremely contrived example, but it illustrates the
problem of using reference types with Validated. It gets even
hairier if i itself were a reference type, like a slice:
void mutateCopiedValue(Validated!(FortyTwo, containsFortyTwo)
fortyTwo)
{
//We're not out of the woods yet
int[] arr = fortyTwo.i;
arr[0] += 1;
}
//Continuing from previous example,
//except i is now an array
mutateCopiedValue(b);
assert(a.i[0] == 44);
assert(b.i[0] == 44);
Obviously in this case you could just .dup i, but what if i were
a class itself? It'd be extremely easy to accidentally invalidate
every Validated!(FortyTwo, ...) in the program in a single swipe.
It gets even worse if i were some class reference to which other,
non-validated references existed. Changing those naked references
would change i, and possibly invalidate it.
More information about the Digitalmars-d
mailing list