Re: Best Way to Pass Template Typed Alias Parameters for Functions?

2018-12-23 Thread Vijay Nayar via Digitalmars-d-learn

On Sunday, 23 December 2018 at 19:10:06 UTC, Alex wrote:

On Sunday, 23 December 2018 at 18:53:15 UTC, Vijay Nayar wrote:
You're right, it does compile. I'm a bit surprised. I wonder 
if this is a relatively recent improvement in the language, 
because last time I ran into this I had no such luck. But 
after seeing that your example did work, I figured one could 
try to get the best of both worlds by using a strongly-typed 
wrapper function in one's class.  So far it seems to work:


import std.traits;

class A(KeyT, alias HashF) {
  // Strongly-typed wrapper around template value parameter 
'HashF'.

  static size_t hash(in KeyT key) {
return HashF(key);
  }
  static this() {
static assert(isCallable!HashF, "Hash function is not 
callable!");
static assert(Parameters!(HashF).length == 1, "Hash 
function must take 1 argument.");

static assert(is(Parameters!(HashF)[0] : const(KeyT)),
"Hash parameter must be const.");
static assert(is(typeof(HashF(KeyT.init)) : size_t),
"Hash function must return size_t type.");
  }

  KeyT data;
  size_t getHash() const {
return hash(data);
  }
}

void main() {
auto a = new A!(int, (int a) => cast(size_t) a);
a.data = 5;
a.getHash();
}


I'm not sure, whether you need the static this() part at all, 
as all of the asserts the compiler should do even when they are 
absent...


by isCallable you restrict the HashF to not use IFTI
by calling HashF(key) you ensure implicitely, that 
Parameters!(HashF).length == 1
by having hash(in KeyT key) defined with an "in" you ensured, 
that HashF does not mutate the argument
and by defining size_t getHash() you ensured the return type of 
HashF...


You are correct again! Playing around with using classes and 
functions returning the wrong type or not having a const 
argument, it seems that the compiler will assure that all the 
conditions needed by the wrapper function are satisfied and give 
a clear error.


I don't know when this happened, but this definitely makes the 
language a lot easier to use for these circumstances, and all the 
std.traits stuff in "static this()" can be thrown out.


Re: Best Way to Pass Template Typed Alias Parameters for Functions?

2018-12-23 Thread Alex via Digitalmars-d-learn

On Sunday, 23 December 2018 at 18:53:15 UTC, Vijay Nayar wrote:
You're right, it does compile. I'm a bit surprised. I wonder if 
this is a relatively recent improvement in the language, 
because last time I ran into this I had no such luck. But after 
seeing that your example did work, I figured one could try to 
get the best of both worlds by using a strongly-typed wrapper 
function in one's class.  So far it seems to work:


import std.traits;

class A(KeyT, alias HashF) {
  // Strongly-typed wrapper around template value parameter 
'HashF'.

  static size_t hash(in KeyT key) {
return HashF(key);
  }
  static this() {
static assert(isCallable!HashF, "Hash function is not 
callable!");
static assert(Parameters!(HashF).length == 1, "Hash 
function must take 1 argument.");

static assert(is(Parameters!(HashF)[0] : const(KeyT)),
"Hash parameter must be const.");
static assert(is(typeof(HashF(KeyT.init)) : size_t),
"Hash function must return size_t type.");
  }

  KeyT data;
  size_t getHash() const {
return hash(data);
  }
}

void main() {
auto a = new A!(int, (int a) => cast(size_t) a);
a.data = 5;
a.getHash();
}


I'm not sure, whether you need the static this() part at all, as 
all of the asserts the compiler should do even when they are 
absent...


by isCallable you restrict the HashF to not use IFTI
by calling HashF(key) you ensure implicitely, that 
Parameters!(HashF).length == 1
by having hash(in KeyT key) defined with an "in" you ensured, 
that HashF does not mutate the argument
and by defining size_t getHash() you ensured the return type of 
HashF...




Re: Best Way to Pass Template Typed Alias Parameters for Functions?

2018-12-23 Thread Vijay Nayar via Digitalmars-d-learn

On Sunday, 23 December 2018 at 18:31:24 UTC, Alex wrote:

On Sunday, 23 December 2018 at 18:13:25 UTC, Vijay Nayar wrote:
For example, if you have a const function in your container 
like "T find() const", and this function needs to use that 
comparator, then you're out of luck because the compiler 
doesn't know that the comparator function is const and will 
not modify the objects being compared.


Last time I ran into this problem, my solution was simply to 
give up on const. But now I'm running into it again and trying 
to think through it again before giving up again.


Hm... still not sure... ;)
This would compile and run:

´´´
import std.experimental.all;

size_t myHashFunction(int a) { return cast(size_t) a; }

void main()
{
auto b = new B!(int, myHashFunction);
b.arr = 42.iota.array;
assert(b.find == 1);
}

class B(T, alias HashF)
{
T[] arr;

T find() const
{
foreach(el; arr)
{
if(HashF(el))
{
return el;
}
}
assert(0);
}
}
´´´


You're right, it does compile. I'm a bit surprised. I wonder if 
this is a relatively recent improvement in the language, because 
last time I ran into this I had no such luck. But after seeing 
that your example did work, I figured one could try to get the 
best of both worlds by using a strongly-typed wrapper function in 
one's class.  So far it seems to work:


import std.traits;

class A(KeyT, alias HashF) {
  // Strongly-typed wrapper around template value parameter 
'HashF'.

  static size_t hash(in KeyT key) {
return HashF(key);
  }
  static this() {
static assert(isCallable!HashF, "Hash function is not 
callable!");
static assert(Parameters!(HashF).length == 1, "Hash function 
must take 1 argument.");

static assert(is(Parameters!(HashF)[0] : const(KeyT)),
"Hash parameter must be const.");
static assert(is(typeof(HashF(KeyT.init)) : size_t),
"Hash function must return size_t type.");
  }

  KeyT data;
  size_t getHash() const {
return hash(data);
  }
}

void main() {
auto a = new A!(int, (int a) => cast(size_t) a);
a.data = 5;
a.getHash();
}


Re: Best Way to Pass Template Typed Alias Parameters for Functions?

2018-12-23 Thread Alex via Digitalmars-d-learn

On Sunday, 23 December 2018 at 18:13:25 UTC, Vijay Nayar wrote:
I've been playing with the idea of specifying the constraints 
or using "static assert" in the constructor. They are good 
options, but there's a few cases where they fall a bit short.  
For example, imagine you have a container class that needs a 
comparator function to be able to compare the items in the 
container.  While you can use std.traits to assure the right 
kind of function is passed in, that information does not make 
its way into the type system.


For example, if you have a const function in your container 
like "T find() const", and this function needs to use that 
comparator, then you're out of luck because the compiler 
doesn't know that the comparator function is const and will not 
modify the objects being compared.


Last time I ran into this problem, my solution was simply to 
give up on const. But now I'm running into it again and trying 
to think through it again before giving up again.


Hm... still not sure... ;)
This would compile and run:

´´´
import std.experimental.all;

size_t myHashFunction(int a) { return cast(size_t) a; }

void main()
{
auto b = new B!(int, myHashFunction);
b.arr = 42.iota.array;
assert(b.find == 1);
}

class B(T, alias HashF)
{
T[] arr;

T find() const
{
foreach(el; arr)
{
if(HashF(el))
{
return el;
}
}
assert(0);
}
}
´´´


Re: Best Way to Pass Template Typed Alias Parameters for Functions?

2018-12-23 Thread Vijay Nayar via Digitalmars-d-learn

On Sunday, 23 December 2018 at 18:04:32 UTC, Alex wrote:

On Sunday, 23 December 2018 at 17:13:49 UTC, Vijay Nayar wrote:
I have a few cases where I would like to pass in a function as 
a value to a template, but I want to ensure that the function 
takes certain kinds of parameters, is a const function, or 
matches any other set of conditions.


I assume you are looking for constraints...
https://dlang.org/concepts.html

Then, case B is the way to go, see
https://run.dlang.io/is/jWU3tr

Case D also looks promising. Not sure, how to formulate the 
specialization right now...


There are a lot of traits you can use for the constraints.
https://dlang.org/phobos/std_traits.html
and
https://dlang.org/spec/traits.html
e.g., for constness there is getFunctionAttributes, which could 
be useful.


I've been playing with the idea of specifying the constraints or 
using "static assert" in the constructor. They are good options, 
but there's a few cases where they fall a bit short.  For 
example, imagine you have a container class that needs a 
comparator function to be able to compare the items in the 
container.  While you can use std.traits to assure the right kind 
of function is passed in, that information does not make its way 
into the type system.


For example, if you have a const function in your container like 
"T find() const", and this function needs to use that comparator, 
then you're out of luck because the compiler doesn't know that 
the comparator function is const and will not modify the objects 
being compared.


Last time I ran into this problem, my solution was simply to give 
up on const. But now I'm running into it again and trying to 
think through it again before giving up again.


Re: Best Way to Pass Template Typed Alias Parameters for Functions?

2018-12-23 Thread Alex via Digitalmars-d-learn

On Sunday, 23 December 2018 at 17:13:49 UTC, Vijay Nayar wrote:
I have a few cases where I would like to pass in a function as 
a value to a template, but I want to ensure that the function 
takes certain kinds of parameters, is a const function, or 
matches any other set of conditions.


What is the best way to do this? Just to avoid any potential 
confusion, I've included a few examples below. I'm using 
explicit functions rather than lambdas just avoid any 
possibility of types being incorrectly inferred.


size_t myHashFunction(int a) { return cast(size_t) a; }

void main() {
class A(KeyT, HashF) { }
auto a = new A!(int, myHashFunction);
// ^ onlineapp.d: Error: template instance `A!(int, 
myHashFunction)`

// does not match template declaration `A(KeyT, HashF)`

// Alias parameter: 
https://dlang.org/spec/template.html#aliasparameters

class B(KeyT, alias HashF) { }
auto b = new B!(int, myHashFunction);
// ^ Works, but we cannot enforce the kind of function 
passed in.


// Typed alias parameter: 
https://dlang.org/spec/template.html#typed_alias_op

class C(KeyT, alias size_t function(KeyT) HashF) { }
auto c = new C!(int, myHashFunction);
// ^ onlineapp.d: Error: template instance `C!(int, 
myHashFunction)`
// does not match template declaration `C(KeyT, alias 
size_t function(KeyT) HashF)`


// Specialization: 
https://dlang.org/spec/template.html#alias_parameter_specialization

class D(KeyT, alias HashF : size_t function(KeyT)) { }
auto d = new D!(int, myHashFunction
// ^ onlineapp.d: Error: template instance `D!(int, 
myHashFunction)`
// does not match template declaration `D(KeyT, alias HashF 
: size_t function(KeyT))`

}

Looking at some of the code in std.algorithm, it seem that 
static asserts are sometimes used for this purpose. Does anyone 
have a solution to this problem of instantiating template 
classes whose parameters are functions?  I have used 
std.function "unaryFun" before, but this has the problem of not 
being able to specify function attributes, like const.


I assume you are looking for constraints...
https://dlang.org/concepts.html

Then, case B is the way to go, see
https://run.dlang.io/is/jWU3tr

Case D also looks promising. Not sure, how to formulate the 
specialization right now...


There are a lot of traits you can use for the constraints.
https://dlang.org/phobos/std_traits.html
and
https://dlang.org/spec/traits.html
e.g., for constness there is getFunctionAttributes, which could 
be useful.