March 08, 2012
Plus I left over some code. Anyway the point is that was just a hardcoded example of something that's doable, you'd probably want it to be much more sophisticated before it goes into a library.
March 08, 2012
I like the way Scala handles this with the Option class. None indicates no value which is equivalent to your null sentinal value but it is a value itself so it is always safe to use.

Combined with pattern matching and the orElse methods makes it very easy to use one variable that both stores the value and at the same time indicates whether it is valid or not. It's not two variables that could get out of sync.
March 08, 2012
On Thu, Mar 08, 2012 at 05:49:20PM +0100, Andrej Mitrovic wrote:
> On 3/8/12, H. S. Teoh <hsteoh@quickfur.ath.cx> wrote:
> > 	foreach (name; __traits(allMembers, typeof(obj))) {
> > 		static if (__traits(compiles, &__traits(getMember, obj,
> > 				name)))
> > 		{
> > 			alias typeof(__traits(getMember, obj, name))
> > 				type;
> > 			static if (is(type==function)) {
> > 				// name refers to a function of type
> > 				// 'type' here
> > 			}
> > 		}
> > 	}
> >
> >> I've never had an easy time interacting with __traits.
> 
> Yesterday I've tried the same thing but it didn't work because I was missing the &__traits call. With that in place, here's a very hardcoded example of what you can do in D:
> 
> http://paste.pocoo.org/show/562933/
> 
> So now you can catch the exception if the object was uninitialized. And you didn't have to modify the target class at all.

Cool! That's a really neat way of doing it. Love the combination of alias this, compile-time introspection, and the awesomeness of D templates. D r0x0rs!


T

-- 
Your inconsistency is the only consistent thing about you! -- KD
March 08, 2012
On 3/8/2012 12:58 AM, Don Clugston wrote:
> The documentation is terrible, but it's really a beautiful design.

I agree, it really is very flexible and useful. The downside is that it leaves significant overhead in functions that are exception-aware, even if they never throw or unwind.

And you're right about the documentation. It's incredibly obtuse, probably the worst I've ever seen.
March 08, 2012
On Thursday, 8 March 2012 at 22:20:10 UTC, Walter Bright wrote:
> I agree, it really is very flexible and useful. The downside is that it leaves significant overhead in functions that are exception-aware, even if they never throw or unwind.

This problem is avoided by the switch to the table-based implementation on x86_64, though, while the flexibility, as far as I know, still remains.

> And you're right about the documentation. It's incredibly obtuse, probably the worst I've ever seen.

Yes – I love how everybody (including me) working on SEH code seems to end up reverse-engineering at least certain parts of it on their own.

David
March 09, 2012
Regarding people that desire a compiler switch to add run-time tests inside the binary that guard against null seg-faults to turn turn them into errors with a stack trace: I have recently shown the D language to a friend. She answered me that most code she writes is for the Web and it's not CPU-bound.

So maybe for her D language is not needed, other more flexible languages are better for her. On the other hand if you already know D and you already use D for other CPU-intensive purposes you may want to use it for situations where a very high performance is not necessary at all.

In such situations you don't compile in release-mode (leaving array bound tests on), and maybe you want null deference exceptions too, and other things (and tolerating some more bloat of the  binary). Things like integral overflow errors and null errors allow you to use D in other niches of the programming landscape, while they do not hurt high performance D code at all because you are able to refuse those things with a compiler switch (the disadvantage is a little more complex compiler, but I think in this case this is acceptable).

Generally not giving such choice to the programmers restricts the applicability and ecological niche of the D language, giving back no gain.

Bye,
bearophile
March 09, 2012
On Friday, 9 March 2012 at 00:19:38 UTC, bearophile wrote:
> So maybe for her D language is not needed, other more flexible languages are better for her.

D rox the web (and has for a while).
March 09, 2012
Adam D. Ruppe:

> D rox the web (and has for a while).

(Oh, you are starting to copy Andrei talk style now :-) The birth of community words, idioms and sub-languages is a very common thing, sociology studies such things a lot).

But there's always some space for improvements :-)

In D.learn there is a thread titled "0 < negative loop condition bug or misunderstanding on my part":

http://forum.dlang.org/thread/tbsvfbotcupussmeticq@forum.dlang.org

Web coders are not going to appreciate such traps. I think integral bound tests at run-time are able to catch part of those problems.

Bye,
bearophile
March 09, 2012
On 03/08/2012 10:40 AM, H. S. Teoh wrote:
> On Thu, Mar 08, 2012 at 02:57:00PM +0100, Andrej Mitrovic wrote:
>
>> I think what Chad is looking for is a BlackHole/WhiteHole equivalent
>> which doesn't need abstract functions but which figures out all the
>> methods of a class at compile time and creates a subclass that
>> throws/does nothing on method invocation. An 'alias this' field would
>> be used that is default-initialized with this sentry object. I don't
>> know why we don't have __traits(allFunction). We have
>> 'getVirtualFunctions' but it requires a function name, but using
>> allMembers to filter out function names is damn difficult if you ask
>> me. I've never had an easy time interacting with __traits.

Yep.  That would be cool.  Although there should be ways of doing the same thing for arrays and possibly even structs.  For structs I wouldn't mind adding a boolean field to keep track of its empty-or-not status.

>
> 	foreach (name; __traits(allMembers, typeof(obj))) {
> 		static if (__traits(compiles,&__traits(getMember, obj,
> 				name)))
> 		{
> 			alias typeof(__traits(getMember, obj, name))
> 				type;
> 			static if (is(type==function)) {
> 				// name refers to a function of type
> 				// 'type' here
> 			}
> 		}
> 	}
>
>> I've never had an easy time interacting with __traits.
>
> Me too. I'm guessing that __traits is the way it is due to ease of
> implementation in the compiler. It's certainly not very friendly to use.
>
>
> T
>

Tried this, but it wasn't picking everything up.

I also suspect that inheriting the class being wrapped and picking on its methods is going to be a losing battle in the long run for this thing.  It doesn't allow the sentry to throw on field access.  It also wouldn't work for final classes.  I wonder if opDispatch would do better.

Another mess I was running into was forwarding templated methods.  I wonder if this is even possible.  I wish __traits had some way of picking up on previous template instantiations (with cycles forbidden, of course).  If __traits were beefed up enough, maybe it would be better than opDispatch after all.  Hmmm.

I did try this as a struct with an "private T t; alias t this;" in it. It looks like this:

-----------------------------------

import std.c.stdlib;
import std.stdio;

struct Emptiable(T)
{
	private static Emptiable!T m_sentinel;
	// private NotNull!T t; // TODO
	private T t;
	
	alias t this;
	
	public static @property Emptiable!T sentinel()
	{
		return m_sentinel;
	}
	
	static this()
	{
		void* sentinelMem = malloc(T.sizeof);
		m_sentinel = cast(T)sentinelMem;
	}
	
	this(A...)(A args)
	{
		t = new T(args);
	}
	
	@property bool isEmpty() const
	{
		if ( this is m_sentinel )
			return true;
		else
			return false;
	}
}

static auto makeEmpty(T)(ref T v)
{
	v = T.sentinel;
	return v;
}

class Bar
{
	int i;
	
	this(int j) { i = j; }
	
	void blah()
	{
		writefln("blah!");
	}
	
	/+void letsTemplate(T)(T f)
	{
		writefln("%s",f);
	}+/
}

void main()
{
	auto bar = Emptiable!(Bar).sentinel;
	auto foo = new Emptiable!Bar(5);
	
	if ( bar.isEmpty )
		writefln("bar is empty, as it should be.");
	
	if ( !foo.isEmpty )
		writefln("foo is full, as it should be.");
	
	//foo.letsTemplate("Just a string.");
	
	writefln("foo.i is %s",foo.i);
	foo.i = 2;
	writefln("foo.i is %s",foo.i);
	
	/+
	makeEmpty(foo);
	if ( foo.isEmpty )
		writefln("foo is now empty.");
	writefln("foo.i is %s",foo.i);
	+/
}

-----------------------------------

Prints:
bar is empty, as it should be.
foo is full, as it should be.
foo.i is 5
foo.i is 2

-----------------------------------

It's super incomplete.  I am starting to realize that this sort of thing leads to a huge amount of weird corner cases.

Also note what happens when it tries to use makeEmpty: it fails because "auto foo = new Emptiable!Bar(5);" allocates an instance of the Emptiable struct on the heap, which then allocates a separate instance of Bar.  Yuck.  It's that old struct-vs-class construction schism again.

March 09, 2012
On 03/09/2012 01:43 AM, bearophile wrote:
> Adam D. Ruppe:
>
>> D rox the web (and has for a while).
>
> (Oh, you are starting to copy Andrei talk style now :-) The birth of community words, idioms and sub-languages is a very common thing, sociology studies such things a lot).
>
> But there's always some space for improvements :-)
>
> In D.learn there is a thread titled "0<  negative loop condition bug or misunderstanding on my part":
>
> http://forum.dlang.org/thread/tbsvfbotcupussmeticq@forum.dlang.org
>
> Web coders are not going to appreciate such traps. I think integral bound tests at run-time are able to catch part of those problems.
>
> Bye,
> bearophile

Comparing signed/unsigned is perfectly reasonable. This is what causes the problem discussed in D.learn:

assert(-1<0U); // fail.