www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - difference between x = Nullable.init and x.nullify

reply vit <vit vit.vit> writes:
Hello,
What's the difference between x = Nullable!Test.init and 
x.nullify?


class Test{}

void test()pure nothrow{
     Nullable!Test x;

     x = Nullable!Test.init; //OK
     x.nullify;              //Error: function 
'std.typecons.Nullable!(Test).Nullable.nullify!().nullify' is not 
nothrow

}
Jun 02 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, June 03, 2017 05:52:55 vit via Digitalmars-d-learn wrote:
 Hello,
 What's the difference between x = Nullable!Test.init and
 x.nullify?


 class Test{}

 void test()pure nothrow{
      Nullable!Test x;

      x = Nullable!Test.init; //OK
      x.nullify;              //Error: function
 'std.typecons.Nullable!(Test).Nullable.nullify!().nullify' is not
 nothrow

 }
Assigning Nullable!Test.init is equivalent to setting the internal value to Test.init and setting _isNull to false. nullify doesn't assign Nullable!Test.init to the Nullable!Test, and it doesn't assign Test.init to _value. Rather, it calls destroy on _value and sets _isNull to true. Exactly what destroy does depends on the type. In the case of a class, it calls rt_finalize on it, which basically calls the class' finalizer (if it has one). But rt_finalize is not nothrow, so destroy isn't nothrow, so nullify isn't nothrow. However, looking at what rt_finalize does, I don't see why it couldn't be nothrow. So, unless I'm missing something, it seems like that would be a good enhancement. - Jonathan M Davis
Jun 02 2017
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis wrote:

 looking at what rt_finalize does, I don't see why it couldn't 
 be nothrow. So, unless I'm missing something, it seems like 
 that would be a good enhancement.

 - Jonathan M Davis
Presently, rt_finalize cannot be made nothrow, or un-made system, because "reasons": http://forum.dlang.org/thread/aalafajtuhlvfirwfvea forum.dlang.org Fixing that would require significant changes to the runtime, and probably the compiler. I don't think it qualifies as a simple "enhancement" :)
Jun 02 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, June 03, 2017 06:41:44 Stanislav Blinov via Digitalmars-d-learn 
wrote:
 On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis wrote:
 looking at what rt_finalize does, I don't see why it couldn't
 be nothrow. So, unless I'm missing something, it seems like
 that would be a good enhancement.

 - Jonathan M Davis
Presently, rt_finalize cannot be made nothrow, or un-made system, because "reasons": http://forum.dlang.org/thread/aalafajtuhlvfirwfvea forum.dlang.org Fixing that would require significant changes to the runtime, and probably the compiler. I don't think it qualifies as a simple "enhancement" :)
Well, as I said, I could be missing something, but all rt_finalize does is call rt_finalize2, and rt_finalize2 _is_ nothrow (it catches any Exceptions that are thrown by the destructor/finalizer). So, I have no idea why it would be the case that rt_finalize couldn't be nothrow, and I saw nothing in that thread which contradicts that, but I could have read it too quickly. Regardless, it's a perfectly valid enhancement request whether it's easy to implement or not. - Jonathan M Davis
Jun 03 2017
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Saturday, 3 June 2017 at 08:01:14 UTC, Jonathan M Davis wrote:
 On Saturday, June 03, 2017 06:41:44 Stanislav Blinov via 
 Digitalmars-d-learn wrote:
 On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis 
 wrote:
 looking at what rt_finalize does, I don't see why it 
 couldn't be nothrow. So, unless I'm missing something, it 
 seems like that would be a good enhancement.

 - Jonathan M Davis
Presently, rt_finalize cannot be made nothrow, or un-made system, because "reasons": http://forum.dlang.org/thread/aalafajtuhlvfirwfvea forum.dlang.org Fixing that would require significant changes to the runtime, and probably the compiler. I don't think it qualifies as a simple "enhancement" :)
Well, as I said, I could be missing something, but all rt_finalize does is call rt_finalize2, and rt_finalize2 _is_ nothrow (it catches any Exceptions that are thrown by the destructor/finalizer). So, I have no idea why it would be the case that rt_finalize couldn't be nothrow, and I saw nothing in that thread which contradicts that, but I could have read it too quickly. Regardless, it's a perfectly valid enhancement request whether it's easy to implement or not. - Jonathan M Davis
Whoops, my bad, I forgot it indeed swallows exceptions and does the onFinalizeError instead. So... yep, then it seems that rt_finalize probably should be marked nothrow too. Hmm... if throwing in a destructor is considered a runtime error, perhaps another valid enhancement would be to statically disallow throwing Exceptions in destructors, i.e. *require* them be nothrow?..
Jun 03 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, June 03, 2017 08:17:18 Stanislav Blinov via Digitalmars-d-learn 
wrote:
 On Saturday, 3 June 2017 at 08:01:14 UTC, Jonathan M Davis wrote:
 On Saturday, June 03, 2017 06:41:44 Stanislav Blinov via

 Digitalmars-d-learn wrote:
 On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis

 wrote:
 looking at what rt_finalize does, I don't see why it
 couldn't be nothrow. So, unless I'm missing something, it
 seems like that would be a good enhancement.

 - Jonathan M Davis
Presently, rt_finalize cannot be made nothrow, or un-made system, because "reasons": http://forum.dlang.org/thread/aalafajtuhlvfirwfvea forum.dlang.org Fixing that would require significant changes to the runtime, and probably the compiler. I don't think it qualifies as a simple "enhancement" :)
Well, as I said, I could be missing something, but all rt_finalize does is call rt_finalize2, and rt_finalize2 _is_ nothrow (it catches any Exceptions that are thrown by the destructor/finalizer). So, I have no idea why it would be the case that rt_finalize couldn't be nothrow, and I saw nothing in that thread which contradicts that, but I could have read it too quickly. Regardless, it's a perfectly valid enhancement request whether it's easy to implement or not. - Jonathan M Davis
Whoops, my bad, I forgot it indeed swallows exceptions and does the onFinalizeError instead. So... yep, then it seems that rt_finalize probably should be marked nothrow too. Hmm... if throwing in a destructor is considered a runtime error, perhaps another valid enhancement would be to statically disallow throwing Exceptions in destructors, i.e. *require* them be nothrow?..
My initial reaction would be that destructors should always be nothrow, though I vaguely recall there being some reason why it was supposed to be nice that destructors in D could cleanly deal with exceptions. And remember that when we're talking about rt_finalize, we're talking about finalizers, not destructors in general. When a destructor is in a GC heap-allocated object, it's treated as a finalizer and may or may not be run (since the object may or may not be collected), whereas when a destructor is on an object that's on the stack, it's really a destructor. So, while they use the same syntax, and in the case of a struct, the same function could be either a destructor or a finalizer depending on where the struct is declared, they're not quite the same thing. And destroy muddies the water a bit, because it then explicitly calls the finalizer on a class, whereas it would normally be the GC that does it (and while calling GC-related functions in a finalizer is forbidden when called by the GC, it's fine when called via destroy, since the GC is then not in the middle of a collection). So, I don't know whether it would be reasonable to require that destructors be nothrow. Certainly, it's _more_ likely for it to be reasonable for destructors on classes to be nothrow, since classes always live on the heap (and are thus finalizers) unless you're playing games with something like std.typecons.scoped, but I'd have to study the matter quite a bit more to give a properly informed answer as to whether it would be reasonable to require that all destructors be nothrow. - Jonathan M Davis
Jun 04 2017
next sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Sunday, 4 June 2017 at 09:04:14 UTC, Jonathan M Davis wrote:

 if throwing in a destructor is considered a runtime error, 
 perhaps another valid enhancement would be to statically 
 disallow throwing Exceptions in destructors, i.e. *require* 
 them be nothrow?..
My initial reaction would be that destructors should always be nothrow, though I vaguely recall there being some reason why it was supposed to be nice that destructors in D could cleanly deal with exceptions. And remember that when we're talking about rt_finalize, we're talking about finalizers, not destructors in general. When a destructor is in a GC heap-allocated object, it's treated as a finalizer and may or may not be run (since the object may or may not be collected),
It doesn't matter. The only thing that matters is that it may be run, and therefore rt_finalize has to count on that. And it sort of does, at the moment, by assuming the worst possible combination of attributes. Problem is, with current language rules, it cannot be any other way, as the runtime doesn't carry any information about attributes of finalized object, or the context in which finalization takes place (i.e. is it within a safe function?), which, sadly, makes unsafe code silently executable in a safe context, in direct contradiction to language guarantees.
 whereas when a destructor is on an object that's on the stack, 
 it's really a destructor. So, while they use the same syntax,
It's worse than that. There are two "destructors": __xdtor that calls destructors of RAII members, and, on classes, __dtor that actually calls ~this() for the class. But only that class, not it's ancestors or descendants. Such segregation is, as it turns out, as useful as it is unwieldy.
 and in the case of a struct, the same function could be either 
 a destructor or a finalizer depending on where the struct is 
 declared, they're not quite the same thing. And destroy muddies 
 the water a bit, because it then explicitly calls the finalizer 
 on a class, whereas it would normally be the GC that does it 
 (and while calling GC-related functions in a finalizer is 
 forbidden when called by the GC, it's fine when called via 
 destroy, since the GC is then not in the middle of a 
 collection).

 So, I don't know whether it would be reasonable to require that 
 destructors be nothrow. Certainly, it's _more_ likely for it to 
 be reasonable for destructors on classes to be nothrow, since 
 classes always live on the heap (and are thus finalizers) 
 unless you're playing games with something like 
 std.typecons.scoped, but I'd have to study the matter quite a 
 bit more to give a properly informed answer as to whether it 
 would be reasonable to require that all destructors be nothrow.
Scoped is not necessary. Classes may not necessarily exist in the GC heap, thanks to custom allocators and emplace(). But because the language does not enforce propagation of destructor attributes, destroy() is system and not nothrow, which spills out into user code that would otherwise take advantage of static inference. Unfortunately, right now making it any other would impose certain restrictions on classes without real language support, and that is... scary.
Jun 04 2017
prev sibling parent reply vit <vit vit.vit> writes:
On Sunday, 4 June 2017 at 09:04:14 UTC, Jonathan M Davis wrote:
 [...]
Why Nullable!T call destroy for reference types?
Jun 04 2017
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, June 04, 2017 09:31:24 vit via Digitalmars-d-learn wrote:
 On Sunday, 4 June 2017 at 09:04:14 UTC, Jonathan M Davis wrote:
 [...]
Why Nullable!T call destroy for reference types?
It calls destroy for everything. Why it does that instead of simply assigning T.init and setting _isNull to true, I don't know. Maybe the commit history would say, but unless it was done as part of a bugfix, it's more likely that you'd have to use the commit history to figure out who made it do that and ask them. Thinking about it though, it does seem like it's probably the wrong behavior. I'd guess that it was done with structs in mind, and it doesn't usually make sense to put a class reference in Nullable outside of generic code, since they can be null on their own. - Jonathan M Davis
Jun 04 2017
prev sibling next sibling parent vit <vit vit.vit> writes:
On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis wrote:
 [...]
thanks
Jun 03 2017
prev sibling parent reply Kagamin <spam here.lot> writes:
On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis wrote:
 Assigning Nullable!Test.init is equivalent to setting the 
 internal value to Test.init and setting _isNull to false.
Eh? Does it mean Nullable is default initialized to some non-null default value?
Jun 03 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Saturday, June 03, 2017 14:30:05 Kagamin via Digitalmars-d-learn wrote:
 On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis wrote:
 Assigning Nullable!Test.init is equivalent to setting the
 internal value to Test.init and setting _isNull to false.
Eh? Does it mean Nullable is default initialized to some non-null default value?
Well, that depends on what you mean by null. Nullable!T doesn't use pointers, so it can't be null like a pointer is null. The whole point of Nullable!T is to emulate the null behavior of pointers while keeping everything on the stack. It's a struct that contains two members: T _value; bool _isNull = true; So, _value is default-initialized to T.init, and _isNull defaults to true. Whether Nullable!T is "null" or not depends on the value of _isNull. So, Nullable!T is default-initialized to null in the sense that _isNull is true, and all of its member functions that check for "null" check whether _isNull is true, so it's treated as "null" when it's default-initialized, but it's not truly null in the sense that a pointer or class reference can be null. If that's what you want, just create a T* rather than a Nullable!T. - Jonathan M Davis
Jun 04 2017
parent reply Kagamin <spam here.lot> writes:
On Sunday, 4 June 2017 at 08:51:44 UTC, Jonathan M Davis wrote:
 On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis 
 wrote:
 Assigning Nullable!Test.init is equivalent to setting the 
 internal value to Test.init and setting _isNull to false.
T _value; bool _isNull = true;
So it was a typo that Nullable.init sets _isNull to false?
Jun 05 2017
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, June 05, 2017 10:46:39 Kagamin via Digitalmars-d-learn wrote:
 On Sunday, 4 June 2017 at 08:51:44 UTC, Jonathan M Davis wrote:
 On Saturday, 3 June 2017 at 06:19:29 UTC, Jonathan M Davis

 wrote:
 Assigning Nullable!Test.init is equivalent to setting the
 internal value to Test.init and setting _isNull to false.
T _value; bool _isNull = true;
So it was a typo that Nullable.init sets _isNull to false?
Yes. - Jonathan M Davis
Jun 05 2017