Thread overview
Using map result type
Dec 08, 2019
AA
Dec 08, 2019
mipri
Dec 10, 2019
AA
Dec 11, 2019
Meta
Dec 11, 2019
Meta
December 08, 2019
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.
December 08, 2019
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);
  }

December 10, 2019
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.
December 10, 2019
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.
December 11, 2019
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));

December 11, 2019
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.