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