New idiom for scala-like implicits while trying to make mixin expressions.

aliak something at something.com
Sat Mar 14 14:03:25 UTC 2020


Hello,

I started out by trying to get mixin templates to work like 
expressions, so something like:

auto a = mixin MyMixin!();

The reason I wanted to do this is so for a cli tool I've been 
working on. But you can't have eponymous template mixins (why is 
that? Does anyone know? Can it be "fixed"), so I found this 
workaround with a very nice added advantage that there's no way 
for the workaround to pollute the calling scope (whereas a mixin 
can). It's basically a way for D code to be given the context of 
the calling scope.

Code:

string f(string callerString = __FUNCTION__)() {
   alias caller = mixin(callerString);
   // caller is an alias to whatever function called f
   return caller.stringof;
}

void h() {
   f.writeln;
}

void g() {
   f.writeln;
}

Basically, I was trying to figure out how to parse the command 
line and allow the parser to know which command line "command" it 
was being called from [0], and then I ended up with this pattern 
for each cli command:

struct Command {
   static immutable help = "help text";
   static immutable command = "cmd1"
   static int run(string[] args) {
     auto parsedOptions = parse(args);
     ...
   }
   struct InnerCommand {
     static immutable help = "help text";
     static immutable command = "cmd2"
     static int run(string[] args) {
       auto parsedOptions = parse(args);
       ...
     }
     ...
   }
   ...

So "parse" above implicitly gets passed the context of the 
function that called it. And from this, you can do:

alias parent = __traits(parent, mixin(caller));

And now "parse" knows which "command struct" it's in, and 
therefore, which sub commands to look for. If you've every used 
docker or kubectl (or any "modern" command line tool) then you 
know what I'm talking about, and if you've tried to build one, 
then you've probably felt the pain and inadequacy of std.getopt, 
at some point that thing becomes useless ... just like react 
native 😝

Of course this reminded me of scala implicits and how it can 
magically get stuff from it's surrounding scope. And I've used 
scala more than I'd like, to know that implicits can make things 
unwieldy and completely obliterate any form of happines an 
engineering team may have. (I believe they're also giving it an 
overhaul for the next scala - aka dotty - they're calling the new 
implicits: "to hell and back again - misery you never thought 
possible").

Basically, the reader of the function "parse" has no clue whats 
happening. This is not scalably maintainable code. So, I wondered 
if a template could fix that:

template context(alais fn, string _caller) {
   alias caller = mixin(_caller);
   auto context(Args...)(auto ref Args args) {
     fn!caller(args);
   }
}

And now the call site becomes:

struct Command {
   ...
   static int run(string[] args) {
     auto parsedOptions = context!parse(args);
     ...
   }
   ...
}

There're a number of problems though:
1) No way to detect overloads because __FUNCTION__ is just the 
name.
2) This won't work for inner structs because you get an error 
like: "no property S for type void" where S is the inner struct 
of a function that is interpreted as "void"
3) Top-level expressions won't work. So this is where __MODULE__ 
might be helpful. I'm still playing around with how to make this 
context thing "public friendly"

I also wonder what kind of other __MACRO__ type macros can be 
used in something like this. I plan on figuring out how to 
implement a Context struct at some point so that the code would 
become like:

template Context(string _caller = __FUNCTION__, string _module = 
__MODULE__) {
   alias caller = mixin(_caller);
   alias module_ = mixin(_module);
}

And then the template "context" would create a Context object and 
pass that in to the function so that:

void someFunction(alias context) {
   pragma(msg, context.caller.stringof):
   pragma(msg, context.module_.stringof):
}

context!someFunction(); // does the magic

So anyway, that's my covid-19-boredom-induced-post-of-the-day. 
Any thoughts?

Cheers,
- ali

[0]: https://dlang.slack.com/archives/C7JB979C7/p1583447534006400


More information about the Digitalmars-d mailing list