Article: Writing Julia style multiple dispatch code in D

jmh530 via Digitalmars-d-announce digitalmars-d-announce at puremagic.com
Fri Aug 25 13:54:05 PDT 2017


On Friday, 25 August 2017 at 16:01:27 UTC, data pulverizer wrote:
>
> Your wrapping strategy looks sensible though I would probably 
> generate them all using string mixins.

See below. I haven't implemented the random variables yet, but 
otherwise it seems to be working well. There is some trickiness 
with deprecated stuff that I had to hard code, but other than 
that it's pretty generic. Also, I think it is ignoring my check 
to only include public/export stuff. Not sure why that is.

module distribAlt;

private template isMemberOf(alias T, string x)
{
     import std.traits : hasMember;

     enum bool isMemberOf = hasMember!(T, x);
}

private void hasMemberCheck(alias T, string x)()
{
     static assert(isMemberOf!(T, x), T.stringof ~ " must have " ~ 
x ~" member to
                                         call " ~ x ~ " function");
}

private string genStructInternals(string funcName, string 
structName)()
{
     import dstats.distrib;
     import std.array : appender;
     import std.algorithm.searching : endsWith;

     enum spaces = "    ";

     auto aliasBuf = appender!string();
     auto importBuf = appender!string();

     enum string invName = "inv" ~ structName;

     enum bool anyPDForPMF = false;

     importBuf.put(spaces);
     importBuf.put("import dstats.distrib : ");

     foreach(member; __traits(allMembers, dstats.distrib))
     {
         static if (__traits(getProtection, member) == "public" ||
                    __traits(getProtection, member) == "export")
         {
             import std.algorithm.searching : startsWith, 
findSplitAfter;
             import std.string : toLower;

             static if (startsWith(member, funcName))
             {
                 enum string memberAfter = findSplitAfter(member, 
funcName)[1];
                 enum string lowerMemberAfter = 
toLower(memberAfter);

                 importBuf.put(member ~ ", ");

                 aliasBuf.put(spaces);
                 aliasBuf.put("alias " ~ lowerMemberAfter ~ " = "
                                                                   
   ~ member ~ ";");
                 aliasBuf.put("\n");

                 static if ((lowerMemberAfter == "pdf") ||
                            (lowerMemberAfter == "pmf"))
                 {
                     aliasBuf.put(spaces);

                     aliasBuf.put("alias density = " ~ 
lowerMemberAfter ~ ";");
                     aliasBuf.put("\n");
                 }
             }
             else static if (startsWith(member, invName))
             {
                 enum string memberAfter = findSplitAfter(member, 
invName)[1];

                 importBuf.put(member ~ ", ");

                 aliasBuf.put(spaces);
                 aliasBuf.put("alias i" ~ toLower(memberAfter) ~ " 
= "
                                                                   
   ~ member ~ ";");
                 aliasBuf.put("\n");
             }
         }
     }

     if (endsWith(importBuf.data, ", "))
     {
         string importOut = importBuf.data[0 .. ($ - (", 
".length))] ~";\n";

         if (endsWith(aliasBuf.data, "\n"))
             return importOut ~ aliasBuf.data[0 .. ($ - 
("\n").length)];
         else
             assert(0, "No relevant functions in dstats.distrib");
     }
     else
     {
         assert(0, "No relevant functions in dstats.distrib");
     }
}

private string toLowerFirst(string name)()
{
     import std.string : toLower;
     import std.conv : to;

     string firstLetter = name[0].toLower.to!string;
     return firstLetter ~ name[1 .. $];
}

private string toUpperFirst(string name)()
{
     import std.string : toUpper;
     import std.conv : to;

     string firstLetter = name[0].toUpper.to!string;
     return firstLetter ~ name[1 .. $];
}

private template GenDistStruct(string name)
{
     const char[] GenDistStruct =
         "///"~ "\n" ~
         "struct " ~ toUpperFirst!(name) ~ "\n" ~
         "{\n" ~
         genStructInternals!(name, toUpperFirst!(name)) ~ "\n" ~
         "}";
}

string GenDistStructs()
{
     import dstats.distrib;
     import std.array : appender;
     import std.algorithm.searching : startsWith, endsWith, 
canFind,
                                                 findSplitBefore, 
findSplitAfter;

     string[__traits(allMembers, dstats.distrib).length] 
createdStructs;
     size_t i;
     auto structsBuf = appender!string();

     foreach(member; __traits(allMembers, dstats.distrib))
     {
         static if (__traits(getProtection, member) == "public" ||
                    __traits(getProtection, member) == "export")
         {
             static if ((member.endsWith("PDF") ||
                             member.endsWith("PMF") ||
                             member.endsWith("CDF") ||
                             member.endsWith("CDFR")))
             {
                 static if (member.endsWith("PDF"))
                     enum string memberBefore =
                                               
findSplitBefore(member, "PDF")[0];
                 else static if (member.endsWith("PMF"))
                     enum string memberBefore =
                                               
findSplitBefore(member, "PMF")[0];
                 else static if (member.endsWith("CDF"))
                     enum string memberBefore =
                                               
findSplitBefore(member, "CDF")[0];
                 else static if (member.endsWith("CDFR"))
                     enum string memberBefore =
                                              
findSplitBefore(member, "CDFR")[0];

                 static if (member.startsWith("inv"))
                     enum string newMember =
                           
toLowerFirst!(findSplitAfter(memberBefore, "inv")[1]);
                 else
                     enum string newMember = memberBefore;

                 static if (member != "chiSqrCDF" &&
                            member != "chiSqrCDFR" &&
                            member != "invChiSqrCDFR" &&
                            member != "invChiSqCDFR") 
//Deprecated: Easiest way I found to fix it
                 {
                     if (i == 0 ||
                                !(createdStructs[0 .. 
i].canFind(newMember)))
                     {
                         structsBuf.put(GenDistStruct!newMember);
                         structsBuf.put("\n");
                         createdStructs[i] = newMember;
                         i++;
                     }
                 }
             }
         }
     }
     return structsBuf.data;
}

mixin(GenDistStructs());

private template GenDistFunc(string name)
{
     const char[] GenDistFunc =
         "auto " ~ name ~ "(alias T, U...)(U u)\n" ~
         "{\n" ~
         `   hasMemberCheck!(T, "` ~ name ~ `");` ~ "\n" ~
         "   return T." ~ name ~ "(u);\n" ~
         "}";
}

mixin(GenDistFunc!("pdf"));
mixin(GenDistFunc!("pmf"));
mixin(GenDistFunc!("cdf"));
mixin(GenDistFunc!("cdfr"));
mixin(GenDistFunc!("icdf"));
mixin(GenDistFunc!("density"));

void main()
{
     import std.stdio : writeln;

     writeln(Normal.cdf(0.5, 0.0, 1.0));
     writeln(cdf!Normal(0.5, 0.0, 1.0));
     writeln(icdf!Normal(cdf!Normal(0.5, 0.0, 1.0), 0.0, 1.0));

     writeln(LogNormal.cdf(0.5, 0.0, 1.0));
     writeln(cdf!LogNormal(0.5, 0.0, 1.0));
}


More information about the Digitalmars-d-announce mailing list