January 11, 2022
On 11.01.22 13:31, RazvanN wrote:
> For example, in the general case, comparing two items
> should not allocate and should not throw;

That's just not true.

> you want to implement something more
> esoteric, that's fine, you can define your own interface.

There we go. You think anything that does not fit those arbitrarily defined interfaces is "esoteric". That's precisely my main issue. This is not true. If someone's use case has to be dubbed "esoteric", it would rather be to use qualifiers beyond `@safe` in object-oriented code.

Note also that there is a huge difference between "does not do X" and "the compiler believes you that you did not do X".
January 11, 2022
On Tuesday, 11 January 2022 at 16:40:01 UTC, Adam D Ruppe wrote:
> On Tuesday, 11 January 2022 at 06:25:34 UTC, Rumbu wrote:
>> More than that, since it is a tight dependency between objects and the garbage collector, what improvement will render a @nogc thrown here and there? Currently there is little support în the language for using objects outside gc
>
> Using objects without the GC is very easy (just put the scope keyword on the variable declaration).

Until now didn't found any use case for stack allocated objects. Most of objects are intended to be passed around and live longer than some local scope. I usually think of them as safe pointers/references and references are meant to be passed as arguments or to be used in composition of other objects, rendering their lifetime dependent of other objects.

If I really need a deterministic destruction or a stacked object, I rather use a struct than a class.

What I meant by lack of language support is missing of built-in object allocation outside the gc. (e.g. emplace).

The bottom line is that I don't find any use in constraining the cmp/eq/hash patterns to @nogc since 99% of use cases will wake up the gc anyway.



January 12, 2022

On Monday, 10 January 2022 at 13:48:14 UTC, Mike Parker wrote:

>

Discussion Thread

This is the discussion thread for the first round of Community Review of DIP 1042, "ProtoObject":

https://github.com/dlang/DIPs/blob/2e6d428f42b879c0220ae6adb675164e3ce3803c/DIPs/DIP1042.md

The review period will end at 11:59 PM ET on January 24, or when I make a post declaring it complete. Discussion in this thread may continue beyond that point.

Here in the discussion thread, you are free to discuss anything and everything related to the DIP. Express your support or opposition, debate alternatives, argue the merits, etc.

General support; but I see no mention of "COM objects", that is a D feature in use in production.

January 13, 2022

I hate the feedback rules.

HS Teoh wrote:

>

This makes me question the wisdom of putting opCmp in the common base of all classes.

Well, this dip changes that.

But a better solution is to just deprecate Object.opCmp. It doesn't harm too much being there (just not generating an error that could be an error) but serves no use and I'd be surprised if anyone would miss it when it's gone, especially since the migration is trivial:

override int opCmp(Object rhs) {
typeof(this) rhsCasted = cast(typeof(this) rhs;
//
}

becomes:

int opCmp(typeof(this) rhs) {
//
}

The deprecation message can easily show you how to do it, and this change is, like with opEquals, possible today and totally backward/forward compatible.

January 13, 2022

On Monday, 10 January 2022 at 13:48:14 UTC, Mike Parker wrote:

>

Discussion Thread

This is the discussion thread for the first round of Community Review of DIP 1042, "ProtoObject":

In reply to this feedback thread post:
https://forum.dlang.org/post/misqawxejcqxqzrrtvhr@forum.dlang.org

Dom DiSc posted the following:

On Monday, 10 January 2022 at 14:44:22 UTC, Elronnd wrote:

>

'cmp' should not return -1 when it can not compare the specified classes. It should not pretend to have a significant result when it does not.

Yep. 'cmp' should return a float (using 4 values: -1, 0, 1 and
NaN). This allows to return NaN if some things are not
comparable. (even better would be if D had a native 2bit type
with exactly those 4 values)

By far the most classes will contain non-comparable values,
because there is only a few "completely ordered" things out
there, and most of them like simple numbers are already
implemented.

January 13, 2022

On Thursday, 13 January 2022 at 00:43:52 UTC, Mike Parker wrote:

>

From Paul Backus:

On Wednesday, 12 January 2022 at 20:26:17 UTC, Dom DiSc wrote:

>

On Monday, 10 January 2022 at 14:44:22 UTC, Elronnd wrote:

>

'cmp' should not return -1 when it can not compare the specified classes. It should not pretend to have a significant result when it does not.

Yep. 'cmp' should return a float (using 4 values: -1, 0, 1 and NaN). This allows to return NaN if some things are not comparable. (even better would be if D had a native 2bit type with exactly those 4 values)

Alternatively: we don't define a global "Comparable" interface at
all, and the compiler handles opCmp on classes the same way it
does for structs, using the static type of the object to
determine which overload to call.

That way, you can have opCmp return int if your class is totally
ordered, or float if it's partially ordered. And you can also
choose whatever function attributes you like.

If you want to write a function that takes "anything comparable"
as an argument, and you don't want to use templates, you can use
a type erasure library like Atila Neves's tardy [1] to convert
objects with different opCmp overloads to a common type that uses
dynamic dispatch under the hood. There is no need to have all of
them inherit from a common interface.

[1] https://code.dlang.org/packages/tardy

January 13, 2022

From H. S. Teoh:

On Wed, Jan 12, 2022 at 08:26:17PM +0000, Dom DiSc via Digitalmars-d wrote:
[...]

>

By far the most classes will contain non-comparable values, because
there is only a few "completely ordered" things out there, and most of
them like simple numbers are already implemented.

This makes me question the wisdom of putting opCmp in the common base of
all classes. If only a small subset of classes will be comparable with
each other, then .opCmp should be relegated to user class hierarchies
where it actually matters. I.e., to classes that implement the
Comparable interface.

The only reason I can think of where one would want to put .opCmp in the
common base class of all classes is so that built-in AA's can function
with arbitrary classes (i.e., so that the AA implementation is not
constrained to algorithms that don't require orderability of class keys
-- e.g., if buckets need to be sorted for efficiency, or the hash algo
uses the ordering for some computations). The current AA implementation
doesn't need .opCmp, though. But even if it did, it wouldn't actually
matter in practice, because if .opCmp is going to return "uncomparable"
for most object pairs, then it's worthless to begin with and unsuitable
to build an AA algorithm with.

January 13, 2022

On Tuesday, 11 January 2022 at 06:25:34 UTC, Rumbu wrote:

>

Comparing strings for example can discover invalid unicode characters, why nothrow?

OTOH you can just just return whatever then. Comparing enums or floating points also does not throw if they are invalid. If you want to provide a throwing variant you can, if you call it something else than cmp.

>

Comparing timestamps may need reading the current time zone, why pure?

Badly designed class, if. The timezone info ought to be already in the stamps when comparing.

January 13, 2022

On Thursday, 13 January 2022 at 17:46:49 UTC, Dukc wrote:

>

On Tuesday, 11 January 2022 at 06:25:34 UTC, Rumbu wrote:

>

Comparing strings for example can discover invalid unicode characters, why nothrow?

OTOH you can just just return whatever then. Comparing enums or floating points also does not throw if they are invalid. If you want to provide a throwing variant you can, if you call it something else than cmp.

>

Comparing timestamps may need reading the current time zone, why pure?

Badly designed class, if. The timezone info ought to be already in the stamps when comparing.

Basically what you said, as the sane thing is working with pure UTC and just offsetting it by the timezone and only actually accounting for timezone when you need to display the time to some user.

January 13, 2022
So let's think about stringify a little.

First, just like with the other things, this isn't a real world problem. You can always tighten constraints in subclasses right now. You can also add overloads to take a sink, like Throwable does, right now. This DIP is worse than nothing.

But let's think about how to do it with virtual functions. Among the options:

1) string toString(); You can override this today with @nogc etc., but it is tricky to actually implement since it returns a string, which is assumed to have no owner. You potentially could return a buffer inside the object, or a malloc'd thing or whatever too, just this goes against the expected conventions and users are liable to misuse it anyway.

I think realistically this signature must either return static data or GC'd data. Of course, it can be `pure nothrow @safe` etc. today with zero trouble, again subclasses are free to add that *today*.

But let's look at the others for nogc options.

2) Throwable adds an overload `void toString(void delegate(in char[]) sink)`

The tricky bit is that sink. Since it isn't labeled @nogc at top level, the implementation has to assume it can be passed a gc func which means the toString itself also can't be @nogc.

This is very similar to opApply right now - you have to add a combination of overloads, and then overriding the right virtual slot in subclasses is tricky. Which one do you override? Which one do you call on the outside?

Of course, this doesn't need to be known by the implementation! Just like `inout`, the implementation could be treated as the most restrictive set, and the actual application at the call site depends on what is actually passed to you.

DIP 1041 discussed exactly this (at significant length):

https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1041.md

It is postponed.... waiting for an answer from our glorious emperor.

Note that any stringify interface is going to face these same questions BECAUSE THERE IS NO LIMITATION FROM OBJECT TODAY. DIP 1042, again, *misidentifies the problem* meaning it cannot fix anything, even if its solutions were sound (and they aren't).

Without something like dip 1041 - not necessarily that exact text, but something along those lines, since it identifies a *real* problem - we're in trouble regardless of if it is on Object, Throwable, ProtoObject Stringify, or anything else.

3) Maybe we could do some kind of return of a builder interface.... something like returning a stringify range that the user must iterate over. If it is turned inside out, it can pass a temporary as `front` and then the user is able to copy it out as needed.

```d
struct StringBuilderRange {
	int delegate (int position, scope char[] buffer) @nogc pure nothrow @safe fillBuffer;
	int position;

	@nogc pure nothrow @safe:

	this(typeof(fillBuffer) fb) {
		fillBuffer = fb;
		popFront();
	}

	void popFront() {
		auto got = fillBuffer(position, buffer[]);
		position += got;
		bufferUsed = got;
	}

	char[] front() return {
		return buffer[0 .. bufferUsed];
	}

	bool empty() const {
		return bufferUsed == 0;
	}

	char[16] buffer;
	int bufferUsed;
}

interface Stringify {
	StringBuilderRange stringify();
}

class Foo : Stringify {
	StringBuilderRange stringify() @nogc nothrow pure @safe return {
		return StringBuilderRange(&this.fillBuffer);
	}

	int fillBuffer(int position, scope char[] buffer) @nogc nothrow pure @safe {
		if(position == 0 && buffer.length >= 5) {
			buffer[0 .. 5] = "hello";
			return 5;
		}

		return 0;
	}
}

void main() @nogc @safe nothrow pure {
	import std.stdio;
	scope foo = new Foo();
	foreach(item; foo.stringify())
		debug writeln(item); // debug just to allow it past nogc pure on main in writeln
}
```


But you can see it is a bit of a pain to implement in the class and this doesn't actually compile with the dip1000. But I don't know how to use that at all so I'm probably just doing it wrong.

Just this kind of turn inside out lets you be very restrictive on the inside without necessarily forcing things to be restrictive on the outside.

This kind of thing MIGHT work with a bit more fleshing out without limiting end users too much. But eeek.

I prefer option #2 here. But that'd dip 1041, not dip 1042.