June 18, 2014
On Wednesday, 18 June 2014 at 19:26:21 UTC, Etienne wrote:
>> Use a maybe monad :
>> Maybe(obj).memeber.nested.val
>>
>> This doesn't require a language construct. Also, if null check are
>> pervasive in your code, you probably have a problem somewhere.
>
> There seems to be an implementation here:
>
> https://bitbucket.org/qznc/d-monad/src/5b9d41c611093db74485b017a72473447f8d5595/generic.d?at=master
>
> It doesn't look as good as what you're suggesting,
>
> auto rht = Applicative!Maybe.right(r1, r2);
>
> auto ii42 = Maybe!(Maybe!int).just(i42);
>
> Any idea how that maybe monad would cascade through into the pointer accessors?

With opDispatch. That is how it is supposed to work.
June 18, 2014
On Wed, Jun 18, 2014 at 03:46:40PM -0400, Etienne via Digitalmars-d wrote:
> >This also allows you to do a complete null check in a single statement:
> >
> >	if (maybe(tree).left.right.right.left.right !is null) {
> >		// do something with that value
> >	}
> >
> >If nothing else, this at least saves you the trouble of having to check every intermediate reference in the chain. :)
> >
> 
> Very clever, this should go straight to std.typecons. I'm definitely ripping it for my project, if you can just scratch something like a boost license on it ;)

Feel free to copy it and do whatever you want with it. It's public domain code, as of right now. :)


T

-- 
Marketing: the art of convincing people to pay for what they didn't need before which you can't deliver after.
June 18, 2014
Fun fact btw: if you are using methods via ufcs you can check for null in there:

class Foo {}

void something(Foo foo) {
   if(foo is null) return;
   foo.something_internal();
}

auto foo = new Foo();
foo.something(); // cool

foo = null;
foo.something(); // still cool
June 18, 2014
On Wednesday, 18 June 2014 at 15:42:04 UTC, Etienne wrote:
>
> writeln(obj.member?.nested?.val);
>

Optional chaining in swift is meant to be used more like this:

if let v = ptr?.attr?.getobj?()?.attr? {
  writeln(v)
} else {
  writeln("oops?!")
}

(I don't think Maybe will look as good.)
June 19, 2014
On Wed, Jun 18, 2014 at 12:36:01PM -0700, H. S. Teoh via Digitalmars-d wrote:
> On Wed, Jun 18, 2014 at 07:04:33PM +0000, via Digitalmars-d wrote:
[...]
> > The expression needs to have exactly one type, and because all of the components can be non-null, it needs to be the type of the last component, in this case `val`. This means that if the one of the components is null, the entire expression needs to return a value of this type, presumably the `.init` value.
> > 
> > The alternative is to raise an exception (or error), but that would
> > defeat the purpose (almost, as it would be slightly better than
> > segfaulting).
> 
> Here's a first stab at a library solution:
[...]

I decided to run some tests on the compiled code to see how performant it was. With dmd, I was unable to get it to inline all the opDispatch calls, even with -O -inline, so the result was rather disappointing. However, with gdc -O3 -finline, all of the opDispatch calls got inlined, and the assembly is the direct equivalent of:

	if (tree !is null)
		if (tree.left !is null)
			...
				writeln(tree.left.right. ... .val);

So this is evidence that even this preliminary implementation already has rather good potential. Perhaps I'll kick up a pull request for it soon. :)


T

-- 
In theory, software is implemented according to the design that has been carefully worked out beforehand. In practice, design documents are written after the fact to describe the sorry mess that has gone on before.
June 19, 2014
On 19/06/14 00:17, Etienne wrote:
>> I want non-null in the type system! To put a nullable reference into a
>> non-null variable you need to check first, but from then on the compiler
>> can ensure it's never null!
>
> That would be great but useless for my situation involving some complex
> AST's
>
> class AEntity
> {
>      Module head;
>      string ident;
>      Type type;
>      AEntity typeInfo;
>      AEntity[] members; // for definitions
>      AEntity[] parameters; // if parameterized
> ...
> }
>
> It's very useful to descend through parts of a complex tree without
> adding plenty of bool checks
>
> foreach (AEntity member; entity.members)
> {
>      if (member.typeInfo !is null)
>      {
>          AEntity curr = member;
>          member.ident ~= member.typeInfo.ident;
>          curr = member.typeInfo;
>          while (curr.typeInfo !is null)
>          {
>              member.ident ~= "." ~ curr.typeInfo.ident;
>              curr = curr.typeInfo;
>          }
>      }
> }

And all of those references can be null at any time? Non-null would force you to initialize some/all of those values to a non-null reference, with the compiler ensuring that that is really the case.

June 19, 2014
On Wednesday, 18 June 2014 at 15:42:04 UTC, Etienne wrote:
> writeln(obj.member?.nested?.val);

You can implement something similar to this using UFCS:

auto q(C)(auto ref C c)
{
    struct Q
    {
        template opDispatch(string name)
        {
            @property auto opDispatch()
            {
                return c ? mixin(q{c.}~name) : null;
            }
        }

        @property auto dummy() { }
    }
    return Q();
}

unittest
{
    class C { C c; }
    C c = new C;
    c.c = new C;

    assert(c.q.c !is null);
    assert(c.q.c.q.c is null);
    assert(c.q.c.q.c.q.c is null);
}
June 19, 2014
On Thursday, 19 June 2014 at 00:36:23 UTC, H. S. Teoh via Digitalmars-d wrote:
> I decided to run some tests on the compiled code to see how performant
> However, with gdc -O3 -finline, all of the opDispatch calls got inlined,
> and the assembly is the direct equivalent of:
>
> 	if (tree !is null)
> 		if (tree.left !is null)
> 			...
> 				writeln(tree.left.right. ... .val);
>

Somebody should blog on this or put it on the front page or something; how many other languages allow a cost-free maybe monad to be implemented in library code?
June 19, 2014
On 6/18/14, 12:26 PM, Etienne wrote:
>> Use a maybe monad :
>> Maybe(obj).memeber.nested.val
>>
>> This doesn't require a language construct. Also, if null check are
>> pervasive in your code, you probably have a problem somewhere.
>
> There seems to be an implementation here:
>
> https://bitbucket.org/qznc/d-monad/src/5b9d41c611093db74485b017a72473447f8d5595/generic.d?at=master
>
>
> It doesn't look as good as what you're suggesting,
>
> auto rht = Applicative!Maybe.right(r1, r2);
>
> auto ii42 = Maybe!(Maybe!int).just(i42);
>
> Any idea how that maybe monad would cascade through into the pointer
> accessors?

opDispatch?
June 19, 2014
On 06/18/2014 09:36 PM, H. S. Teoh via Digitalmars-d wrote:
> Here's a first stab at a library solution:
>
> 	/**
> 	 * Simple-minded implementation of a Maybe monad.
> 	 *

Nitpick: Please do not call it a 'Maybe monad'.
It is not a monad: It's neither a functor not does it have a μ operator. (This could be fixed though.) Furthermore, opDispatch does not behave analogously to a (restricted) monadic bind operator:

class C{ auto foo=maybe(C.init); }

void main(){
    import std.stdio;
    C c=new C;
    writeln(maybe(c).foo); // Maybe(Maybe(null))
}

The result should be Maybe(null), if the data type was to remotely resemble a monad.

Furthermore, 'Maybe' is a more natural name for a type constructor that adds an additional element to another type, and 'Maybe monad' in particular is a term that already refers to this different meaning even more strongly in other communities.