www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Can you introspect predicate arity and if it's an equality predicate?

reply aliak <something something.com> writes:
Hi, so basically is there a way to:

void func(alias pred = null, Range)(Range range) {
   // 1) check if pred(ElementType!Range.init, 
ElementType!Range.init) is equality
   // 2) check if isUnary!pred
   // 3) check if isBinary!pred
}

I think maybe the isUnary or isBinary may not work unless it 
takes an extra parameter that gives it some context, i.e. 
isBinary!(pred, T, U) where T and U are the parameter types.

Currently I have this for an isUnary

template isUnary(alias pred) {
     static if (is(typeof(pred) : string))
     {
         enum isUnary = is(typeof(unaryFun!pred(0)));
     }
     else static if (is(Parameters!pred F) && F.length == 1)
     {
         enum isUnary = true;
     }
     else
     {
         enum isUnary = false;
     }
}

But that does not seem to work in this situation:

isUnary!(a => a) // returns false

It won't work if I do isUnary!"a(2)" for e.g, in the case that T 
is callable or isUnary!"a.x" in the case that "a" is a struct 
with a member x ... just to name a couple of cases.

If I give context then all's good:

enum isUnaryOver = is(typeof(unaryFun!pred(T.init))); // works

isEqualityFunction Im not sure how to go about. So open to any 
ideas, or if there're already some traits or something I can use. 
I was thinking I could do:

alias E = // some element type
static if (isBinary!(pred, E) && binaryFun!pred(E.init, E.init) 
== true)
{
   // is equality ... but the for double.init is NaN == NaN ?
   // and probably some other things I'm not thinking about.
}

Advice and/or suggestions?

Cheers

PS: the purpose of the isEqualityFunction is to determine whether 
I should use the predicate for sorting or for just for searching, 
so:

f!"a < b"(range) // range is sorted and then operated on
f!"a == b"(range) // range is not sorted and is operated on by 
leveraging pred
Jan 11 2018
next sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Friday, 12 January 2018 at 00:16:07 UTC, aliak wrote:
 Hi, so basically is there a way to:

 void func(alias pred = null, Range)(Range range) {
   // 1) check if pred(ElementType!Range.init, 
 ElementType!Range.init) is equality
   // 2) check if isUnary!pred
   // 3) check if isBinary!pred
 }
For 2 and 3, there's std.traits.arity. However, as you point out, it has some problems with templated functions. This template will handle those cases in conjunction with std.traits.arity: import std.traits : arity, isCallable; template arity(alias Fn, Args...) if (is(typeof(Fn!Args)) && isCallable!(typeof(Fn!Args))) { enum arity = .arity!(Fn!Args); } unittest { assert(arity!(() => 1) == 0); assert(arity!(a => a, int) == 1); assert(arity!((a,b) => a, int, float) == 2); void test(T)(T,T,T) {} assert(arity!(test, int) == 3); } Checking if a function is the equality function is much harder. Consider this function: bool compare(int a, int b) { if (a == 2 && b = 3) return true; return a == b; } Clearly this is not an equality function, but for the vast majority of inputs, it behaves exactly the same. There are other examples that make this hard. It's perfectly possible to overload opEquals and have it ignore some fields, or even access a database on the other side of the atlantic ocean. In these cases, evaluating the equality of two objects is not testable at compile time, and you simply cannot know. In your examples you use string functions ("a == b", e.g.). These can of course be compared (you might want to do some processing, as "a==b" should give the same result as "b == a"). There's cases here where you can't be sure, of course. All in all, I believe you're trying to solve the wrong problem, but I might be wrong. Care to give a bit more information so I get a better idea of why you want to do this? -- Simen
Jan 12 2018
parent aliak <something something.com> writes:
On Friday, 12 January 2018 at 08:18:02 UTC, Simen Kjærås wrote:
 On Friday, 12 January 2018 at 00:16:07 UTC, aliak wrote:
 Hi, so basically is there a way to:

 void func(alias pred = null, Range)(Range range) {
   // 1) check if pred(ElementType!Range.init, 
 ElementType!Range.init) is equality
   // 2) check if isUnary!pred
   // 3) check if isBinary!pred
 }
For 2 and 3, there's std.traits.arity. However, as you point out, it has some problems with templated functions. This template will handle those cases in conjunction with std.traits.arity: import std.traits : arity, isCallable; template arity(alias Fn, Args...) if (is(typeof(Fn!Args)) && isCallable!(typeof(Fn!Args))) { enum arity = .arity!(Fn!Args); } unittest { assert(arity!(() => 1) == 0); assert(arity!(a => a, int) == 1); assert(arity!((a,b) => a, int, float) == 2); void test(T)(T,T,T) {} assert(arity!(test, int) == 3); }
Thank you! That leading period too ... noice!
 Checking if a function is the equality function is much harder. 
 Consider this function:

 bool compare(int a, int b) {
     if (a == 2 && b = 3) return true;
     return a == b;
 }

 Clearly this is not an equality function, but for the vast 
 majority of inputs, it behaves exactly the same.

 There are other examples that make this hard. It's perfectly 
 possible to overload opEquals and have it ignore some fields, 
 or even access a database on the other side of the atlantic 
 ocean. In these cases, evaluating the equality of two objects 
 is not testable at compile time, and you simply cannot know.

 In your examples you use string functions ("a == b", e.g.). 
 These can of course be compared (you might want to do some 
 processing, as "a==b" should give the same result as "b        
 ==      a"). There's cases here where you can't be sure, of 
 course.

 All in all, I believe you're trying to solve the wrong problem, 
 but I might be wrong. Care to give a bit more information so I 
 get a better idea of why you want to do this?
Nah, your gut was spot on :D What I was trying to do didn't really make any sense. I'm learning D by playing with generic algorithms (trying to recreate a javascript functional library called lodash), and was trying to be "too smart". My thought process was I want a function that finds the intersection between two arrays, and I want to allow the user to pass in an equality predicate as well, in which case the intersection will return elements that return true for the equality predicate, and the algorithm would then not care about sortedness and do a naive O(n*m) approach (ie: for each element in a check if it exists in b and return it as one of the intersection elements). If the predicate was not an equality predicate than it'd be assumed to be the predicate that was used to sort the array (in the case that they're sorted) and then a faster O(n) algorithm can be used. The unary/binary stuff was to determine if the predicate was a unary predicate, in which case a transformation is applied to each elements and you have an algorithm that returns the transformed intersected elements. Realized that the binary equality predicate is just a special case of the unary transformation predicate so I actually don't need to (and obviously as you point out can't reliable) test that a binary predicate is an equality one. Cheers, and thanks for your input!
Jan 12 2018
prev sibling parent reply Seb <seb wilzba.ch> writes:
On Friday, 12 January 2018 at 00:16:07 UTC, aliak wrote:
 Hi, so basically is there a way to:

 void func(alias pred = null, Range)(Range range) {
   // 1) check if pred(ElementType!Range.init, 
 ElementType!Range.init) is equality
   // 2) check if isUnary!pred
   // 3) check if isBinary!pred
 }

 [...]
It isn't possible yet, but will be very soon: https://github.com/dlang/dmd/pull/7484
Jan 12 2018
next sibling parent cuxu <cuxu kumail8.info> writes:
мinterest Ask. our service 
https://reviews-up.com/android-app-reviews/ with the help of 
specialists will help you in promoting your application and 
solving this issue
Jan 12 2018
prev sibling parent reply aliak <something something.com> writes:
On Friday, 12 January 2018 at 10:55:53 UTC, Seb wrote:
 On Friday, 12 January 2018 at 00:16:07 UTC, aliak wrote:
 Hi, so basically is there a way to:

 void func(alias pred = null, Range)(Range range) {
   // 1) check if pred(ElementType!Range.init, 
 ElementType!Range.init) is equality
   // 2) check if isUnary!pred
   // 3) check if isBinary!pred
 }

 [...]
It isn't possible yet, but will be very soon: https://github.com/dlang/dmd/pull/7484
Thats to compare whether lambdaA == lambdaB right? Not if lambda(a, b) == lambda(b, a) == true <- i guess this is not possible anyway without knowing the entire set of inputs)
Jan 12 2018
parent Seb <seb wilzba.ch> writes:
On Friday, 12 January 2018 at 13:04:30 UTC, aliak wrote:
 On Friday, 12 January 2018 at 10:55:53 UTC, Seb wrote:
 On Friday, 12 January 2018 at 00:16:07 UTC, aliak wrote:
 Hi, so basically is there a way to:

 void func(alias pred = null, Range)(Range range) {
   // 1) check if pred(ElementType!Range.init, 
 ElementType!Range.init) is equality
   // 2) check if isUnary!pred
   // 3) check if isBinary!pred
 }

 [...]
It isn't possible yet, but will be very soon: https://github.com/dlang/dmd/pull/7484
Thats to compare whether lambdaA == lambdaB right? Not if lambda(a, b) == lambda(b, a) == true <- i guess this is not possible anyway without knowing the entire set of inputs)
Yes. The implementation is actually pretty straight-forward: Serialize to AST and compare.
Jan 12 2018