www.digitalmars.com         C & C++   DMDScript  

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

reply aliak <something something.com> writes:
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
Mar 14 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 14 March 2020 at 14:03:25 UTC, aliak wrote:
 workaround with a very nice added advantage that there's no way 
 for the workaround to pollute the calling scope (whereas a 
 mixin can).
This scares me, since "polluting" the calling scope is the entire point of a template mixin - to mix in its members.
   return caller.stringof;
Similarly, any use of stringof is a red flag to me. Super buggy and indicates a good chance you are trying to pound a square peg into a round hole. So let me look at your big picture goal and see if there's an entirely different approach....
 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:
I'd either make the `parse` function be a member of the struct (which can be mixed in inside the struct if you like) or just simply be passed an instance/object. mixin template Parse() { ParsedOptions parse(string[] args) { // note that you can simply use `this` in here! } } struct Command { /* stuff */ mixin Parse; } Anywhere you mixin as the member function, the parse function will see the appropriate `this` and can go off on that. However, that won't work top-level, since a module doesn't have `this`. I'm OK with that; it is simple enough to wrap commands in a struct anyway. But you could also abstract that a wee bit more too and have like static if(is(typeof(this)) alias t = this; // use this else alias t = mixin(__MODULE__); get reference to module // use t from here on. but the wrapper is easy enough to me, I like putting all the commands in a struct anyway. Or alternatively, of course, you can: auto parsedOptions = parse!(typeof(this))(args); // explicitly pass context In either case, it doesn't know which specific function it is called from, but it knows the object, which is what it really wants anyway! Then you can look at the members, including sub-commands to go down the whole tree. You could also explicitly pass `parse!(__traits(parent, {}))(args)` to pass the specific function.
Mar 14 2020
parent reply aliak <something something.com> writes:
On Saturday, 14 March 2020 at 15:34:53 UTC, Adam D. Ruppe wrote:
 On Saturday, 14 March 2020 at 14:03:25 UTC, aliak wrote:
 workaround with a very nice added advantage that there's no 
 way for the workaround to pollute the calling scope (whereas a 
 mixin can).
This scares me, since "polluting" the calling scope is the entire point of a template mixin - to mix in its members.
Why does it scare you? I know pollution is the point of a mixin, and mixin is the only way in D to get the context. And I'm not a fan of pollution, which is why a solution that doesn't pollute a scope is preferred - yes you cam be careful to avoid accidental pollution. But you can't guarantee it. If you want certain functionality, and your two options is one that involves possible pollution and one that doesn't, I'd always take the non-pollution route.
   return caller.stringof;
Yeah agreed.
 So let me look at your big picture goal and see if there's an 
 entirely different approach....
There were a number of approaches I went through first 1) Mixin template * Awkward syntax (mixin a thing, then know what the thing is that was mixed in) * Could potentially put unwanted stuff in my scope * No way for code-reader to know how the mixin is changing the scope, making maintainability more cumbersome. You can go and look it up, but it's likely you'll miss something and also having to look up things for no reason is annoying when avoidable. * Having to always mixin something into your struct if you can get it automated seems unnecessary. Sure you can do it, but why when it can be avoided? 2) Explicitly passing in the context * Copy/pasta error prone. * Extra typing when maybe unnecessary * You can pass in the wrong context and the parse function will never be the wiser The mixin soluton I tried was in place, not in the struct scope though. As you've already pointed out, putting in a mixin at struct level will not work because the top level is a function (but that can be worked around)
 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:
I'd either make the `parse` function be a member of the struct (which can be mixed in inside the struct if you like) or just simply be passed an instance/object. mixin template Parse() { ParsedOptions parse(string[] args) { // note that you can simply use `this` in here! } } struct Command { /* stuff */ mixin Parse; } Anywhere you mixin as the member function, the parse function will see the appropriate `this` and can go off on that. However, that won't work top-level, since a module doesn't have `this`. I'm OK with that; it is simple enough to wrap commands in a struct anyway. But you could also abstract that a wee bit more too and have like static if(is(typeof(this)) alias t = this; // use this else alias t = mixin(__MODULE__); get reference to module // use t from here on. but the wrapper is easy enough to me, I like putting all the commands in a struct anyway. Or alternatively, of course, you can: auto parsedOptions = parse!(typeof(this))(args); // explicitly pass context
That also wouldn't work in main().
 In either case, it doesn't know which specific function it is 
 called from, but it knows the object, which is what it really 
 wants anyway! Then you can look at the members, including 
 sub-commands to go down the whole tree.

 You could also explicitly pass `parse!(__traits(parent, 
 {}))(args)` to pass the specific function.
Hehe yeah, tried that too :) That'd work in main() but not inside a Command struct. This'll work though: int a; parse!(__traits(parent, __traits(parent, a))); Courtesy of Seven from the slack link.
Mar 14 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 14 March 2020 at 16:42:11 UTC, aliak wrote:
 Why does it scare you?
It just indicates to me that there might be a better way.
   * Having to always mixin something into your struct if you 
 can get it automated seems unnecessary. Sure you can do it, but 
 why when it can be avoided?
You're manually writing the function call, which is unnecessary too, so the question is just where you want to write it. It is possible to do this kind of thing with only one explicit "parse" or "runCommand" or whatever call for the entire program, with the rest being done automatically through reflection. --- help("help text") command("cmd1") struct Command { bool option; // options for this command string whatever; int opCall() { // execute implementation return 0; } help("more help text") command("cmd2") struct InnerCommand { int opCall() { // do whatever } } } int main(string[] args) { // or you could list it manually or whatever return runCommand!(getMembersByUda!(mixin(__MODULE__), command))(args); } --- So then all your implementations are in one function, your options are simple members, subcommands are nested (or can be external and aliased in there btw), no more writing `parse` separately at all - no mixin, no needing to know context. And you can also call the commands from code pretty easily. Command cmd; cmd.option = true; cmd(); or run an individual one with strings runCommand!Command(["--option"]); and so on. Moving all that logic outside makes the code the most succinct.
Mar 14 2020
parent aliak <something something.com> writes:
On Saturday, 14 March 2020 at 19:00:52 UTC, Adam D. Ruppe wrote:
 On Saturday, 14 March 2020 at 16:42:11 UTC, aliak wrote:
 Why does it scare you?
It just indicates to me that there might be a better way.
I think I may have missed what scares you. Was it that mixins cause pollution or that I thought not causing pollution was an advantage or was it that I thought of mixins? (or something else?)
   * Having to always mixin something into your struct if you 
 can get it automated seems unnecessary. Sure you can do it, 
 but why when it can be avoided?
You're manually writing the function call, which is unnecessary too, so the question is just where you want to write it. It is
In regards to the previous suggestion from the last post, you're mixing in the parse function, then calling it (2 steps). Whereas the alternative was just calling it. This next suggestion with reflection through is indeed "stepless", but a lot more setup work and is indeed another way to go. Now the difference becomes whether you want the options spread out all over a struct or contained at the call-site of parse. My first thought is to have them contained because these structs can get pretty big depending on the number of sub-commands.
 possible to do this kind of thing with only one explicit 
 "parse" or "runCommand" or whatever call for the entire 
 program, with the rest being done automatically through 
 reflection.

 ---
  help("help text")
  command("cmd1")
 struct Command {
   bool option; // options for this command
   string whatever;

   int opCall() {
         // execute implementation
         return 0;
   }

    help("more help text")
    command("cmd2")
   struct InnerCommand {
       int opCall() {
           // do whatever
       }
   }
 }

 int main(string[] args) {
    // or you could list it manually or whatever
    return runCommand!(getMembersByUda!(mixin(__MODULE__), 
 command))(args);
 }
 ---

 So then all your implementations are in one function, your 
 options are simple members, subcommands are nested (or can be 
 external and aliased in there btw), no more writing `parse` 
 separately at all - no mixin, no needing to know context.

 And you can also call the commands from code pretty easily.

 Command cmd;
 cmd.option = true;
 cmd();

 or run an individual one with strings

 runCommand!Command(["--option"]);

 and so on. Moving all that logic outside makes the code the 
 most succinct.
Mar 14 2020