August 29, 2013 assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Hello all, Within std.random there are various constructs, such as RandomCover or RandomSample, where it's desirable for the internal state not to be initialized until right before the first call to .front (or one of the other public methods). Some rationale is given here: http://forum.dlang.org/post/mailman.581.1377772931.1719.digitalmars-d@puremagic.com http://d.puremagic.com/issues/show_bug.cgi?id=10322 Now, this raises a general question for me. Within code we have two alternative means of error-checking -- assert(), for checking for logical errors in pre-release code, and enforce(), for essential checks that must be enforced even in release code. We have an equivalent to assert() for structs/classes in the form of invariant(), which will make various checks at entry and exit to public functions, but -- if I'm not wrong -- this gets discarded in -release mode. So, is there a case for an equivalent to enforce() -- something that will _always_ be checked on entry/exit to a public method or property? I'd extend that by saying I think it might be a good idea to split that in two, so that one can define entry and exit conditions, but I'm more interested in what everyone thinks of the general idea. It could be very useful for the issues identified with std.random, but I'm not sure how it might impact on things like e.g. function properties. For example, if this enforced entry condition checks for initialization and makes sure initialization takes place of it hasn't already, it would surely destroy the possibility of const for any struct/class method, no? So, I thought I'd throw the idea out there so that others can suggest whether this is potentially useful or whether it's better/safer to hand-code such checks on a function-by-function basis. Thanks & best wishes, -- Joe |
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Joseph Rushton Wakeling | On 2013-08-29 13:19, Joseph Rushton Wakeling wrote: > So, I thought I'd throw the idea out there so that others can suggest > whether this is potentially useful or whether it's better/safer to > hand-code such checks on a function-by-function basis. You can quite easily create a wrapper for that. struct EnforceWrapper (T) { T t; auto opDispatch (string name, Args ...) (Args args) { // before code mixin("auto result = " ~ name ~ "(args);"); // after code return result; } } auto obj = EnforceWrapper!(Object)(new Object); obj.toString(); -- /Jacob Carlborg |
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On 08/29/13 15:26, Jacob Carlborg wrote: > On 2013-08-29 13:19, Joseph Rushton Wakeling wrote: > >> So, I thought I'd throw the idea out there so that others can suggest whether this is potentially useful or whether it's better/safer to hand-code such checks on a function-by-function basis. Hard-coded unconditional invariants does not seem very useful, and, as Jacob points out, can be done by wrapping the type. That of course creates a new type; if the checks are really supposed to be always-on, then it isn't a problem. > You can quite easily create a wrapper for that. > > struct EnforceWrapper (T) > { > T t; > > auto opDispatch (string name, Args ...) (Args args) > { > // before code > mixin("auto result = " ~ name ~ "(args);"); > // after code > return result; > } > } > > auto obj = EnforceWrapper!(Object)(new Object); > obj.toString(); In order to handle ref-returns it would have to be more like struct EnforceWrapper(T) { T t; auto ref opDispatch(string M, Args...)(Args args) { {/*before code*/} scope (exit) {/*after code*/} return mixin("t." ~ M ~ "(args)"); } } and need another two overloads for property/field getters and setters. Handling ref-args would add more complexity, but in many cases ignoring them or using auto-ref is enough. artur |
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
On 29/08/13 16:21, Artur Skawina wrote:
> In order to handle ref-returns it would have to be more like
>
> struct EnforceWrapper(T) {
> T t;
>
> auto ref opDispatch(string M, Args...)(Args args) {
> {/*before code*/}
> scope (exit) {/*after code*/}
> return mixin("t." ~ M ~ "(args)");
> }
> }
>
> and need another two overloads for property/field getters and
> setters. Handling ref-args would add more complexity, but in
> many cases ignoring them or using auto-ref is enough.
Interesting. When it comes down to it, it looks like what you're proposing is an extended version of Proxy, no .... ?
|
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
On 08/29/13 16:55, Joseph Rushton Wakeling wrote:
> On 29/08/13 16:21, Artur Skawina wrote:
>> In order to handle ref-returns it would have to be more like
>>
>> struct EnforceWrapper(T) {
>> T t;
>>
>> auto ref opDispatch(string M, Args...)(Args args) {
>> {/*before code*/}
>> scope (exit) {/*after code*/}
>> return mixin("t." ~ M ~ "(args)");
>> }
>> }
>>
>> and need another two overloads for property/field getters and setters. Handling ref-args would add more complexity, but in many cases ignoring them or using auto-ref is enough.
>
> Interesting. When it comes down to it, it looks like what you're proposing is an extended version of Proxy, no .... ?
Maybe, no idea what Proxy does.
'opDispatch' will work for simple cases, and in situations like the one you described, where you just want a bit of sugar for a /local/ always-on invariant, have full control over the wrapped type, and want to eliminate the boilerplate that would otherwise be required.
A more generic solution would be something like:
struct EnforceWrapper(T) {
T t;
mixin wrapMembers!t;
void _before(string M, A...)() {...}
void _after(string M, A...)() {...}
}
where the 'wrapMembers` mixin-template examines 't' and generates
the necessary wrappers. That way overload sets will keep working, etc.
artur
|
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Artur Skawina | Let me share some thoughts and inner warnings, here. I notice a certain (constructive and pragmatic) tendency here to say "Ha, that can be done" using templates, mixins, whatever. I personally feel that to be somewhat dangerous and short term. It's about concepts. Concepts and a smart and consistent implementation is almost always preferable over a "can be done" hack. DbC is a concept and a powerful one - and the D creators have wisely decided to implement it in D. Unfortunately they seem to not have done it consistently (as some question and threads indicate). There *should* be a clear answer/rule/statement to "which one to use? enforce? invariants? asserts?". And there *should* be a way to let the programmer decide about his use of DbC (as opposed to "release cuts all DbC"). I propose to end the "release cuts out DbC" approach and to instead introduce sth. like "@DbC-on" and '@DbC-off'. This, hand in hand with D's smart version mechanism offers much more without losing anything. The current way would be version(release) @DbC-off; and being the default D would work as it did before. But we could decide ourselves and we could have DbC available at runtime if we please so. Using @DbC-on and @DbC-off we could even have good granularity. assert shouldn't be mixed up with DbC (as it often is right now). In fact, I think, assert could/should have two major roles. Ignoring DbC assert has a well established purpose and that's what it has and does in D, too. In relation/context to DbC there is no competition. DbC cares about three scenarios, namely, method entry, method exit and class level ("as long as you are not in a method X is guaranteed to be ..."). assert is no competion but a complement as I see it, in that it offers checks/guarantees for certain single situations that are just not DbC jobs (example: make sure inside a method something doesn't go amok). Thanks for considering A+ -R |
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Joseph Rushton Wakeling | On 2013-08-29 16:55, Joseph Rushton Wakeling wrote: > Interesting. When it comes down to it, it looks like what you're > proposing is an extended version of Proxy, no .... ? It is a proxy. What's the point of a proxy if not to add some additional functionality. -- /Jacob Carlborg |
August 29, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On 29/08/13 21:44, Jacob Carlborg wrote: > It is a proxy. What's the point of a proxy if not to add some additional > functionality. No, I mean std.typecons.Proxy: http://dlang.org/phobos/std_typecons.html#.Proxy The point is that std.typecons.Proxy _just_ wraps the underlying type; it doesn't, so far as I can see, allow you to define extra functionality that must be implemented whenever a public method of the underlying type is called. |
August 30, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Joseph Rushton Wakeling | On 2013-08-29 23:16, Joseph Rushton Wakeling wrote: > No, I mean std.typecons.Proxy: > http://dlang.org/phobos/std_typecons.html#.Proxy > > The point is that std.typecons.Proxy _just_ wraps the underlying type; > it doesn't, so far as I can see, allow you to define extra functionality > that must be implemented whenever a public method of the underlying type > is called. Oh, that Proxy. What's the point of it? The example looks like just an "alias this". -- /Jacob Carlborg |
August 30, 2013 Re: assert() vs. enforce(), invariant() vs. ... ? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jacob Carlborg | On Friday, 30 August 2013 at 06:34:58 UTC, Jacob Carlborg wrote:
> On 2013-08-29 23:16, Joseph Rushton Wakeling wrote:
>
>> No, I mean std.typecons.Proxy:
>> http://dlang.org/phobos/std_typecons.html#.Proxy
>>
>> The point is that std.typecons.Proxy _just_ wraps the underlying type;
>> it doesn't, so far as I can see, allow you to define extra functionality
>> that must be implemented whenever a public method of the underlying type
>> is called.
>
> Oh, that Proxy. What's the point of it? The example looks like just an "alias this".
alias this, but without implicit conversion. It just implements all the op**** functions (including opDispatch).
|
Copyright © 1999-2021 by the D Language Foundation