Null-Safe Dereference Operator

Adam D. Ruppe via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Mar 13 21:00:03 PDT 2017


On Tuesday, 14 March 2017 at 01:08:50 UTC, Jonathan M Davis wrote:
> It does not, though if you really wanted to, you could probably 
> create template that did the same thing fairly easily.

I recently added something similar to dom.d, since I wanted to 
pull a header if present, and was ok with a null string if there 
wasn't one.

I used to write:

string header;
if(auto e = document.querySelector("h1"))
    header = e.innerText;

but now i can write:

string header = document.optionSelector("h1").innerText;

http://dpldocs.info/experimental-docs/arsd.dom.Element.optionSelector.html


Since the selector syntax is already a DSL for descending into 
the tree, you don't typically need to chain it far, but the 
optionSelector magic allows it anyway:

// all of this becomes null-safe dereferencing...
document.optionSelector("h1").firstChild.nextSibling.innerText



The return value is a new type:

http://dpldocs.info/experimental-docs/arsd.dom.MaybeNullElement.html

That uses opDispatch and type checking to wrap. Here is the 
complete source code, that you might be able to adapt to your 
object too, or make it REALLY generic and use it anywhere:

---
struct MaybeNullElement(SomeElementType) {

	this(SomeElementType ele) {

		this.element = ele;

	}

	SomeElementType element;


	/// Forwards to the element, wit a null check inserted that 
propagates null.
	auto opDispatch(string method, T...)(T args) {
		alias type = typeof(__traits(getMember, element, method)(args));
		static if(is(type : Element)) {
			if(element is null)
				return MaybeNullElement!type(null);
			return __traits(getMember, element, method)(args);
		} else static if(is(type == string)) {
			if(element is null)
				return cast(string) null;
			return __traits(getMember, element, method)(args);
		} else static if(is(type == void)) {
			if(element is null)
				return;
			__traits(getMember, element, method)(args);
		} else {
			static assert(0);
		}
	}

	/// Allows implicit casting to the wrapped element.
	alias element this;
}
---




Now, the C# thing is cool because you can use it with any object, 
but in my experience, I don't *need* it with most objects. I 
found I mostly want it with XML and JSON, so I just adapted those 
two specific classes to allow something like the above and now 
get a decent amount of mileage out of it without needing the 
general-purpose operator.

You can templatize that MaybeNull thing to work on arbitrary 
objects too, then write like 
`NullSafe(obj).chain.as.much.as.you.want` if you like, but 
another benefit of me finding I mostly wanted it on just those 
two things is I made a shortcut method: document.optionSelector 
rather than `nullSafe(document.querySelector)` or whatever.

You could also make some kind of UFCS null safe chainer:

obj.ns.foo.ns.bar

where there is a NullSafe!T ns(T obj) {} that returns a null-safe 
wrapper; a user-defined function taking the place of the language 
operator.



So, no, D doesn't have the C# thing, but there are a few other 
options you can explore to do something similar.


More information about the Digitalmars-d-learn mailing list