How to use std.meta.Filter?

Jonathan M Davis newsgroup.d at jmdavisprog.com
Sat Apr 21 18:16:30 UTC 2018


On Saturday, April 21, 2018 17:46:05 Dr.No via Digitalmars-d-learn wrote:
> On Saturday, 21 April 2018 at 17:15:47 UTC, Jonathan M Davis
>
> wrote:
> > On Saturday, April 21, 2018 16:05:22 Dr.No via
> >
> > Digitalmars-d-learn wrote:
> >> import std.meta : Filter;
> >> enum isNotReservedSymbol(string name) = name != "none" && name
> >> !=
> >> "lastToken";
> >> enum string[] members = staticMembers!Token;
> >> static foreach(member; Filter!(isNotReservedSymbol, members))
> >> {{
> >>
> >> This return the error:
> >>   Error: template instance `pred!(["none", "word", "n",
> >>
> >> "digits",
> >> "name", /* my whole array here */ ])  does not match template
> >> declaration isNotReservedSymbol(string name)
> >>
> >> how should have isNotReservedSymbol be defined?
> >
> > std.meta.Filter operates on an AliasSeq, not a dynamic array.
> > If you have an array, then you can just use
> > std.algorithm.iteration.filter with a normal lambda.
> >
> > - Jonathan M Davis
>
> I've tried use normal filter - albeit i'm willing to do all that
> at runtin, but I got f cannot be read at compile time.
>
> static foreach(member; staticMembers!Token.filter!(f =>
> isNotReservedSymbol!(member))

Well, it would help if you actually provide an example that I could compile.
Based on what you've posted thus far, I assume that staticMembers!Token
results in a dynamic array of strings. So, if I have

import std.algorithm.iteration : filter;

enum names = ["none", "word", "n", "digits", "name"];

enum isNotReservedSymbol(string name) = name != "none" &&
                                        name != "lastToken";

static foreach(member; names.filter!(f => isNotReservedSymbol!member))
{
}

void main()
{
}

then I have something similar to what you have, and I get the error

q.d(7): Error: undefined identifier member
/usr/local/include/dmd/std/algorithm/iteration.d(1106):        instantiated 
from here: FilterResult!(__lambda6, string[])
q.d(7):        instantiated from here: filter!(string[])

The problem with this is that member is being used on both the left and the
right of the foreach. You can't use the iteration variable in what you're
iterating. So, I'm guessing that you really meant to use f on the right-hand
side of the lambda. In that case, it's

static foreach(member; names.filter!(f => isNotReservedSymbol!f))
{
}

and the error message becomes

q.d(7): Error: variable f cannot be read at compile time
q.d(7):        while looking for match for isNotReservedSymbol!(f)
/usr/local/include/dmd/std/algorithm/iteration.d(1106):        instantiated 
from here: FilterResult!(__lambda6, string[])
q.d(7):        instantiated from here: filter!(string[])

And that error message is more like what you mentioned, so I'm guessing that
this is what you're hitting. The problem here is that you're mixing up
runtime and compile-time constructs. Even though all of this is happening at
compile time, there's a difference between a purely compile-time construct
and a runtime construct that's run during CTFE. e.g. something like

bool foo(string str)
{
    return isNotReservedSymbol!str;
}

cannot compile, becuase str is a runtime parameter and thus its value is
only known at runtime, whereas isNotReservedSymbol is a template, so its
argument must be known during compile time - i.e. when the functions is
compiled. Using foo with CTFE doesn't change that. foo still needs to
compile as if it were going to be used at runtime. The resulting function
can then be used at compile time, but it needs to be compiled first. Your
lambda has the same problem. Generally, the two options in a case like this
are to either turn the parameter into a compile-time parameter or to use a
function instead of a template. e.g.

bool foo(string str)()
{
    return isNotReservedSymbol!str;
}

or

bool isNotReservedSymbol(string name)
{
    return name != "none" && name != "lastToken";
}

bool foo(string str)
{
    return isNotReservedSymbol(str);
}

Given that you're operating on a dynamic array of strings, there's really no
reason to declare templates to operate on it. Stuff like std.meta.Filter is
for cases where you need to use an AliasSeq, which _usually_ means that
you're dealing with actual symbols and not values (occasionally, it can make
sense to use an AliasSeq with values, but that's usually only if they're not
the same type or if the AliasSeq contains a mix of types and values, since
if an AliasSeq containts values of the same type, you can always just put []
around the AliasSeq to get a dynamic array of its values). So, I'd suggest
that you simply make isNotReservedSymbol a function. And then you get
something like

import std.algorithm.iteration : filter;

enum names = ["none", "word", "n", "digits", "name"];

bool isNotReservedSymbol(string name)
{
    return name != "none" && name != "lastToken";
}

static foreach(member; names.filter!(f => isNotReservedSymbol(f)))
{
}

void main()
{
}

and that compiles.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list