with and checked with expression?

ArturG via Digitalmars-d digitalmars-d at puremagic.com
Tue May 24 12:03:11 PDT 2016


ive read some discussions about some library solution for the ?. 
operator,
has there been anything added to phobos?

wouldnt a checked with expression be a more general/longer form 
of the ?. operator?

A with expression not only allows you to do unchecked/checked 
access to members but to any operation you can do on the type,
it allows method/statement cascading so that you dont have to 
return this from methods,
thereby you can combine it with function chaining without 
breaking the cascading,
and it can also be used to wrap any statement to integrate them 
into a function chain.


some example syntax:

    T t = T # { /*same as with statement*/ };   // works only with 
aggregate types, returns its input

    T t = T #aliasName {  };   // works with any type, can use 
aliasName to refer to the input like self/this, returns its input

    RetType t = T #aliasName, RetType varName {  };   // same as 
above but can change the return type

    // extention for the above, checks the input before executing 
the block,
    // if input is null/false the block does'nt get executed the 
return value will be null/T.init/RetType.init

    T t = T #? {  };

    T t = T #?aliasName {  };

    RetType t = T #?aliasName, RetType varName {  };

    // optionally

    #{ /*empty with expression*/ };  // originally not planned but 
would work like the statement expression
                                     // mentioned in the kill the 
comma operator thread, no input no output and allways runs block

    // for single operations, the short null checked version is 
less verbose

    t?.foo = 5;   // if t is null no assignment happens,
    // same as if(t) t.foo = 5; or t#?{ foo = 5; };

    t.child = s?.child;  // if s is null, t.child will be null,
    // same as t.child = s? s.child : null; or t.child = s#?_, 
typeof(t.child) ret{ ret = child;};

    t?.child = s?.child?;   // if t is null no assignment, if s or 
s.child is null, t.child will be null,
    // same as if(t) t.child = (s && s.child)? s.child : null; or 
t#?lhs{ s#? { child#?rhs{ lhs.child = rhs; };};};

-----------------------------------------
some examples:

    // using it with aggregate types

    struct Foo{ string name; int val; }
    class Bar{ string name; int val; }

    Foo()#{ name = "Foo"; val = 5; }.writeln;

    new Foo() #
    {
       import std.string : capitalize;
       name = someName.capitalize;
       val = 5;
    }.writeln;

    auto foo = Foo()#n{ name = "Foo"; val = 5; n.writeln; };

    new Bar#{ name = "Foo"; val = 5; }.writeln;
    auto bar = new Bar#{ name = "Foo"; val = 5; };

    auto someBar = (someCond? getBar() : new Bar) #{ name = "Bar"; 
val = 30; };
    auto someBar = getBar.or(new Bar) #{ name = "Bar"; val = 30; };

    auto dg = (string s, int v) => new Bar#{ name = s; val = v; };


    // some other random uses

    "test"#n{ import std.string; n.capitalize; }.writeln;

    66#n, string ret{ import std.conv; ret = n.to!string; }.foo();

    // writes the return value of someFun
    someFun()#n{ n == 5? doSomething() :
                 n == 6? doSomethingElse() : null; }.writeln;

    // writes five or six or no match
    someFun()#n, string ret{ ret = n == 5? "five" :
                                   n == 6? "six"  : "no match"; 
}.writeln;

    auto res = someFun.someOtherFun #n{ foreach(i; 0 .. n) 
i.writeln; }.fun(6) #n{ n.writeln; };

    //void fun(string name, int val, string, int);

    wrap!fun#{ name = "Test"; param2 = "some string";
               param3 = 66; val = 2; }();

    auto wrappedFun = wrap!fun#{
                          name = "test";
                          param2 = "some string";
                          param3 = 66;
                          val = 2;
                      };
    wrappedFun.param0 = "Test";
    wrappedFun.param2 = "some other string";
    wrappedFun.val = 70;
    wrappedFun();


    // if(c.foo) c.foo.x = 55;
    c.foo?.x = 55;
    c.foo#?{ x = 55; };


    // if(someClass && someClass.child && someClass.child.child &&
    //    someOtherClass && someOtherClass.child && 
someOtherClass.child.child)
    // { someClass.child.child.x = someOtherClass.child.child.x * 
2; }

    someClass?.child?.child?.x = someOtherClass?.child?.child?.x * 
2;

    someClass#?{ child#?{ child#?lhs {
       someOtherClass#?{ child#?{ child#?rhs {
       lhs.x = rhs.x * 2;
    };};};};};};


    //if(someClass) with(someClass){
    //   x = 5; x.writeln;
    //   if(child) with(child){
    //      x = 6; x.writeln;
    //      alias lhs = child;
    //      if(child) with(child){
    //         if(someOtherClass) with(someOtherClass){
    //            x = 10; x.writeln;
    //            if(child) with(child){
    //               x = 59; x.writeln;
    //               if(child) with(child){
    //                  lhs.x = x * 2;
    //                  x = 44; x.writeln;
    //} } } } } }

    // same as above
    someClass #? { x = 5; x.writeln;
       child  #? { x = 6; x.writeln;
          child #?lhs {
             someOtherClass #? { x = 10; x.writeln;
                child #? { x = 59; x.writeln;
                   child #? { lhs.x = x * 2; x = 44; x.writeln;
    }; }; }; }; }; };



    // empty with expression, when you dont need a input and 
output basically just a scope as a expression
    for(size_t i; i < 10; #{ i++; i.writeln; })
    {}


here some templates which implement parts of the above behavior

    import std.functional : unaryFun;

    auto then(alias fun, T)(T type) if(is(typeof(unaryFun!fun)))
    {
       import std.traits : ReturnType;
       static if(is(ReturnType!(fun!T) == void))
       {
          fun(type);
          return type;
       }
       else
       {
          return fun(type);
       }
    }

    auto checkThen(alias fun, T)(T type) 
if(is(typeof(unaryFun!fun)))
    {
       import std.traits : ReturnType, isFloatingPoint;
       static if(is(ReturnType!(fun!T) == void))
       {
          static if(isFloatingPoint!T)
          {  import std.math : isNaN;
             if(type.isNaN) return type;
          }

          if(type) fun(type);
          return type;
       }
       else
       {
          static if(isFloatingPoint!T)
          {  import std.math : isNaN;
             if(type.isNaN) return ReturnType!(fun!T).init;
          }

          return type? fun(type) : ReturnType!(fun!T).init;
       }
    }

example comparing the above templates and with expression:
original example from dlangui using mostly dml: 
https://github.com/buggins/dlangui/blob/master/examples/helloworld/src/helloworld.d

with expression: https://dpaste.dzfl.pl/15f767d91212
templates: https://dpaste.dzfl.pl/5648204131c8

would something like this be usefull?


More information about the Digitalmars-d mailing list