Thread overview
New idiom for scala-like implicits while trying to make mixin expressions.
Mar 14, 2020
aliak
Mar 14, 2020
Adam D. Ruppe
Mar 14, 2020
aliak
Mar 14, 2020
Adam D. Ruppe
Mar 14, 2020
aliak
March 14, 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
March 14, 2020
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.

March 14, 2020
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.


March 14, 2020
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.
March 14, 2020
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.