making a struct an inputRange with free functions

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Apr 19 01:33:26 UTC 2018


On Wednesday, April 18, 2018 20:39:46 jmh530 via Digitalmars-d-learn wrote:
> On Monday, 16 April 2018 at 19:27:28 UTC, Jonathan M Davis wrote:
> > [snip]
>
> It really would be nice if it worked with free functions...
>
> I was trying to get the example working with Atila's concepts
> library [1]. I tried re-writing the checkInputRange function so
> that UFCS is only used when hasMember passes, but that didn't
> seem to help things. The big issue is when the free functions are
> in another module that checkInputRange does not know about. The
> only solution I can think of is a template mixin (below). Then to
> use, you import InputRange; mixin InputRange; . However, I have
> no idea how well this will work more generally.
>
>
>
> mixin template InputRange()
> {
>      void checkInputRange(R)(inout int = 0) {
>          R r = R.init;     // can define a range object
>          if (r.empty) {}   // can test for empty
>          r.popFront;       // can invoke popFront()
>          auto h = r.front; // can get the front of the range
>      }
>      enum isInputRange(R) = is(typeof(checkInputRange!R));
> }
>
> [1] https://github.com/atilaneves/concepts

Occasionally, that aspect of importing and UFCS can be annoying, but on the
whole, I don't really see why it matters much, particularly when actually
having the free functions available would be an enormous change to how
imports work and could cause a ton of other problems. It would mean that
code would be affected by which code imported it rather than just the code
that it imports, and at that point, you effectively lose control over what's
going on. It would mean that just like C/C++ #includes, you couldn't rely on
the module being the same every time you imported it. Even if the effect
were limited to templated code, it would mean that two supposedly identical
instantiations of a template would not necessarily be identical anymore, and
they'd have to be recompiled in every module that used them. It would be a
disaster in the making. mixins are the closest that we get to that, but in
that case, the programmer is specifically stating that they want to reuse
that code directly in their own module as if it were declared there rather
than using stuff from other modules. Occasionally, that might be limiting,
but without those restrictions, you basically don't have a module system
anymore. And with mixins, you have control over what affects your module,
whereas having the code that imports a module affect it would be more like
mixing in code from the outside.

And in any case, IMHO, the range API functions aren't really functions that
make much sense as free functions anyway. They're not generic, and they're
very much tied to the type that they go with - just like opEquals, opAssign,
or toString are tied to the type. They're inherently pretty much the
opposite of generic. And even if the import rules somehow let you have them
as free functions without it causing problems, what would it buy you? The
only situation I can think of where it might be useful is if you're dealing
with a type that you can't control and thus can't add the member functions
to. But in that case, you can always just wrap that type in another type
that does declare the range API. So, I don't think that much is lost by not
being able to use UFCS to make something a range.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list