www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - explicit castable to bool for predicate restraints?

reply "monarch_dodra" <monarchdodra gmail.com> writes:
I had a discussion a little while ago, regarding the validation 
of predicates.

Basically, fearing that someone may write a predicate that 
returned some weird user-defined type that wasn't bool-able, I 
wrote:

//----
void foo(Range, S)(Range r, S s)
    if (isForwardRange!Range && is(typeof(r.front == s) : bool))
{ ... }

or

void foo(alias pred, Range, S)(Range r, S s)
    if (isForwardRange!Range && is(typeof(unaryFun!pred(r.front)) 
: bool))
{ ... }
//----

I just realized that the problem with this (which I need to fix 
asap in phobos, since I was recently pulled), is that this test 
will fail if the predicate returned something that was not 
*implicitly* castable to bool, but still *explicitly* castable to 
bool. For example:

//----
int cmp()
{
     return 4;
}

void main()
{
     static if (cmp())
         static assert(is(typeof(cmp()) : bool)); //Here
}
//----

This code will enter the static if (which means the result of cmp 
*can* be used inside an if), however, it will fail the assert 
(here).

Conclusion: using `is(typeof(bla) : bool)` is bad form, as many 
(valid) comparators (and even opEquals) return int.

So the "better" test would be to test with cast(bool), as such:
//----
void foo(Range, S)(Range r, S s)
    if (isForwardRange!Range && is(typeof(cast(bool)(r.front == 
s)))
{ ... }

or

void foo(alias pred, Range, S)(Range r, S s)
    if (isForwardRange!Range && 
is(typeof(cast(bool)unaryFun!pred(r.front))))
{ ... }
//----

So here are my two questions:

1. Is there a more "elegant" way to write this test? Is there a 
more convenient "is explicitly castable syntax"?

2. Is this test even worth it? It bogs down the restriction 
(which needs to be as_clear_as_possible for our callers). Could 
we just say "Phobos expects any and all predicates to be useable 
in an if", and not bother?

On the one hand, it needs testing, but on the other, the check's 
"cognitive cost" could be too high...

Maybe we could have the functions match on any predicate return, 
but then consider that an invalid return type is a checked error 
(static assert)?

//----
void foo(Range, S)(Range r, S s)
    if (isForwardRange!Range && is(typeof(r.front == s))
{
     static assert(typeof(cast(bool)(r.front == s)), "The supplied 
predicate does not return a testable result.");
     ...
}
//----

Opinions? Just want to know how which direction to take my future 
developments.
Dec 17 2012
next sibling parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Monday, December 17, 2012 10:23:45 monarch_dodra wrote:
 Opinions? Just want to know how which direction to take my future
 developments.

How about we just require that they all return bool? I see no reason to support anything else. - Jonathan M Davis
Dec 17 2012
prev sibling next sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 17 December 2012 at 23:40:19 UTC, Jonathan M Davis 
wrote:
 On Monday, December 17, 2012 10:23:45 monarch_dodra wrote:
 Opinions? Just want to know how which direction to take my 
 future
 developments.

How about we just require that they all return bool? I see no reason to support anything else. - Jonathan M Davis

That seems excessive to me. I think a lot of predicates, especially if they have C linkage, will return a (u)int whose values are just 0 or 1. I see no reason to not support this.
Dec 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, December 18, 2012 10:23:18 monarch_dodra wrote:
 On Monday, 17 December 2012 at 23:40:19 UTC, Jonathan M Davis
 
 wrote:
 On Monday, December 17, 2012 10:23:45 monarch_dodra wrote:
 Opinions? Just want to know how which direction to take my
 future
 developments.

How about we just require that they all return bool? I see no reason to support anything else. - Jonathan M Davis

That seems excessive to me. I think a lot of predicates, especially if they have C linkage, will return a (u)int whose values are just 0 or 1. I see no reason to not support this.

Then wrap them in a lambda. int is not a boolean value in D, and I really don't think that we should be treating it as such. No D functions should be returning int for a boolean value, and I would expect using C functions with std.algorithm to be quite rare. - Jonathan m Davis
Dec 18 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, December 18, 2012 01:45:14 Jonathan M Davis wrote:
 On Tuesday, December 18, 2012 10:23:18 monarch_dodra wrote:
 On Monday, 17 December 2012 at 23:40:19 UTC, Jonathan M Davis
 
 wrote:
 On Monday, December 17, 2012 10:23:45 monarch_dodra wrote:
 Opinions? Just want to know how which direction to take my
 future
 developments.

How about we just require that they all return bool? I see no reason to support anything else. - Jonathan M Davis

That seems excessive to me. I think a lot of predicates, especially if they have C linkage, will return a (u)int whose values are just 0 or 1. I see no reason to not support this.

Then wrap them in a lambda. int is not a boolean value in D, and I really don't think that we should be treating it as such. No D functions should be returning int for a boolean value, and I would expect using C functions with std.algorithm to be quite rare.

We're probably stuck with std.algorithm allowing implict conversions to bool due to the fact that a fair bit of it has accepted that for a while, but accepting anything which explictly casts to bool would be tantamount to turning explicit casts into implicit casts and would be very dangerous. The language is _very_ restrictive about where it does that precisely because of all the problems caused by implicit conversions in C++. Conditions inside of if statements and loops are the only place in the language where an explicit cast is implicitly done. And it's somewhat surprising that it does even that given how restrictive it is about implicit conversions in general. Everywhere else, an explicit cast is required to get an explicit cast. It's trivial to create a lambda to wrap functions which require explicit casts. - Jonathan M Davis
Dec 18 2012
prev sibling parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 18 December 2012 at 10:08:12 UTC, Jonathan M Davis 
wrote:
 We're probably stuck with std.algorithm allowing implict 
 conversions to bool
 due to the fact that a fair bit of it has accepted that for a 
 while, but
 accepting anything which explictly casts to bool would be 
 tantamount to
 turning explicit casts into implicit casts and would be very 
 dangerous.

 [SNIP]

 - Jonathan M Davis

Hum... For now, I'll simply remove the newly enforced restrictions then, as it may break existing code. I'll put in more thinking before doing anything else.
Dec 18 2012