www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Using map result type

reply AA <duser fastmail.com> writes:
I'd like to accept the return type of map. From some previous 
questions that I should accept a template?
So for something like:

```
void mapAccepter(Range)(Range r)
{
     import std.array : array;
     import std.stdio : writeln;

     auto collected = r.array;
     writeln(collected);
}

void main()
{
     import std.algorithm.iteration : map;

     int[] nums = [1, 2, 3];
     auto evenness = map!(n => n % 2 == 0)(nums);
     mapAccepter(evenness);
}
```

1) Is there any way I can make `mapAccepter` not a templated 
function?
2) Is there any way if I need to make `mapAccepter` templated to 
constrain Range to be a range of booleans.
Dec 07 2019
next sibling parent reply mipri <mipri minimaltype.com> writes:
On Sunday, 8 December 2019 at 01:10:21 UTC, AA wrote:
 I'd like to accept the return type of map. From some previous
 questions that I should accept a template?
In general this is what you want to do with any kind of range code, because you're not working with definite types, but with types that have certain properties. And an advantage of this is that you can decide to do more or less efficient things at compile time based on additional properties of the types you're working with. Just open up phobos and look for 'static if': https://github.com/dlang/phobos/blob/master/std/algorithm/searching.d#L3858 So if you you give minPos an array, it'll just foreach() over the array and return a slice. In which case minPos is exactly as efficient as the code you would've written by hand instead of using minPos, because it expands to that same code. And if you give minPos something else, it'll still work.
 So for something like:

 ```
 void mapAccepter(Range)(Range r)
 {
     import std.array : array;
     import std.stdio : writeln;

     auto collected = r.array;
     writeln(collected);
 }

 void main()
 {
     import std.algorithm.iteration : map;

     int[] nums = [1, 2, 3];
     auto evenness = map!(n => n % 2 == 0)(nums);
     mapAccepter(evenness);
 }
 ```
 1) Is there any way I can make `mapAccepter` not a templated
 function?
Not really. Even if you try to use typeof() to get the (internal) type that std.alogorithm.map returns, you'll run into errors like Error: function x253.mapAccepter(MapResult!(__lambda5, int[]) r) is not callable using argument types (MapResult!(__lambda1, int[])) when you try to use it.
 2) Is there any way if I need to make `mapAccepter` templated
 to constrain Range to be a range of booleans.
Certainly. Static reflection's what this kind of generic code is all about. See all the constraints in the std.algorithm pages in the library documentation? Just look at this one, randomly picked: https://dlang.org/phobos/std_algorithm_searching.html#.endsWith if (isBidirectionalRange!Range && (Needles.length > 1) && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1..$])) : uint)); So for your code: void mapAccepter(Range)(Range r) if (is(ElementType!Range == bool)) { import std.array : array; import std.stdio : writeln; auto collected = r.array; writeln(collected); } Or if you do this often, maybe something like this would do: enum RangeOfBools(T) = is(ElementType!T == bool) && isInputRange!T; void mapAccepter(Range)(Range r) if (RangeOfBools!Range) { import std.array : array; import std.stdio : writeln; auto collected = r.array; writeln(collected); }
Dec 07 2019
parent reply AA <duser fastmail.com> writes:
On Sunday, 8 December 2019 at 01:43:43 UTC, mipri wrote:
 On Sunday, 8 December 2019 at 01:10:21 UTC, AA wrote:
 [...]
In general this is what you want to do with any kind of range code, because you're not working with definite types, but with types that have certain properties. And an advantage of this is that you can decide to do more or less efficient things at compile time based on additional properties of the types you're working with. Just open up phobos and look for 'static if': https://github.com/dlang/phobos/blob/master/std/algorithm/searching.d#L3858 So if you you give minPos an array, it'll just foreach() over the array and return a slice. In which case minPos is exactly as efficient as the code you would've written by hand instead of using minPos, because it expands to that same code. And if you give minPos something else, it'll still work.
 [...]
 [...]
Not really. Even if you try to use typeof() to get the (internal) type that std.alogorithm.map returns, you'll run into errors like Error: function x253.mapAccepter(MapResult!(__lambda5, int[]) r) is not callable using argument types (MapResult!(__lambda1, int[])) when you try to use it.
 [...]
Certainly. Static reflection's what this kind of generic code is all about. See all the constraints in the std.algorithm pages in the library documentation? Just look at this one, randomly picked: https://dlang.org/phobos/std_algorithm_searching.html#.endsWith if (isBidirectionalRange!Range && (Needles.length > 1) && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1..$])) : uint)); So for your code: void mapAccepter(Range)(Range r) if (is(ElementType!Range == bool)) { import std.array : array; import std.stdio : writeln; auto collected = r.array; writeln(collected); } Or if you do this often, maybe something like this would do: enum RangeOfBools(T) = is(ElementType!T == bool) && isInputRange!T; void mapAccepter(Range)(Range r) if (RangeOfBools!Range) { import std.array : array; import std.stdio : writeln; auto collected = r.array; writeln(collected); }
Thanks for the detailed reply. So maybe it is just my perspective coming from other languages that I was expecting a class i.e. having some interface based way of dealing with the return type. But I guess isInputRange checks for structural equality rather than class and doesn't encode that into any interfaces in std library normally? Would the second solution of declaring a template constraint like that be considering strange/out of place in D? e.g. do people normally try and declare the template constraints on a function or just rely on compile time failure from to instantiate template.
Dec 09 2019
parent Dominikus Dittes Scherkl <dominikus.scherkl continental-corporation.com> writes:
On Tuesday, 10 December 2019 at 07:23:56 UTC, AA wrote:

 Would the second solution of declaring a template constraint 
 like that be considering strange/out of place in D? e.g. do 
 people normally try and declare the template constraints on a 
 function or just rely on compile time failure from to 
 instantiate template.
No, you can do "normal" OOP as you like. But you will encounter the constraint approach often in phobos and other libraries, as is provides some advantages.
Dec 10 2019
prev sibling parent reply Meta <jared771 gmail.com> writes:
On Sunday, 8 December 2019 at 01:10:21 UTC, AA wrote:
 I'd like to accept the return type of map. From some previous 
 questions that I should accept a template?
 So for something like:

 ```
 void mapAccepter(Range)(Range r)
 {
     import std.array : array;
     import std.stdio : writeln;

     auto collected = r.array;
     writeln(collected);
 }

 void main()
 {
     import std.algorithm.iteration : map;

     int[] nums = [1, 2, 3];
     auto evenness = map!(n => n % 2 == 0)(nums);
     mapAccepter(evenness);
 }
 ```
 1) Is there any way I can make `mapAccepter` not a templated 
 function?
Yes (mostly, anyway), using the interfaces in std.range.interfaces: https://dlang.org/phobos/std_range_interfaces.html import std.algorithm; import std.range; void mapAccepter(E)(InputRange!E r) { import std.array: array; import std.stdio: writeln; auto collected = r.array; writeln(collected); } void main() { int[] nums = [1, 2, 3]; auto evenness = inputRangeObject(map!(n => n % 2 == 0)(nums)); mapAccepter(evenness); } `mapAccepter` still needs to be templated on the element type of the range, but there are ways to avoid that as well, if desired. I wouldn't recommend it, however, as it wouldn't be that useful in this case.
 2) Is there any way if I need to make `mapAccepter` templated 
 to constrain Range to be a range of booleans.
Yup, there are two ways that you you primarily do that. Either constraining E in the template declaration, or adding a constraint on the template. Generally option 2 is the more idiomatic D way. Option 1: constrain E to be of type bool: void mapAccepter(E: bool)(InputRange!E r); OR void mapAccepter(E)(InputRange!E r) if (is(E == bool)); There's not much difference between these two, but the latter is probably preferred. Option 2: use std.traits and a template constraint: void mapAccepter(E)(InputRange!E r) if (is(ElementType!r == bool));
Dec 11 2019
parent Meta <jared771 gmail.com> writes:
On Wednesday, 11 December 2019 at 20:08:37 UTC, Meta wrote:
 import std.algorithm;
 import std.range;

 void mapAccepter(E)(InputRange!E r)
 {
     import std.array: array;
     import std.stdio: writeln;

     auto collected = r.array;
     writeln(collected);
 }

 void main()
 {
     int[] nums = [1, 2, 3];
     auto evenness = inputRangeObject(map!(n => n % 2 == 
 0)(nums));
     mapAccepter(evenness);
 }
I guess I should mention, that if you are expecting a range of booleans, you can of course write mapAccepter as a non-templated function: void mapAccepter(InputRange!bool r); But if you want to support any type of input range, the function needs to at least be templated on the element type of the range.
Dec 11 2019