www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - assert() vs. enforce(), invariant() vs. ... ?

reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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
Aug 29 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
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
Aug 29 2013
next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
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
Aug 29 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
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
Aug 29 2013
parent reply Jacob Carlborg <doob me.com> writes:
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
Aug 29 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-30 12:56, John Colvin wrote:

 alias this, but without implicit conversion. It just implements all the
 op**** functions (including opDispatch).

So what's the difference to Typedef, declared just below? -- /Jacob Carlborg
Aug 30 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-08-30 14:18, John Colvin wrote:

 Typedef uses Proxy to do the work. Proxy is a mixing template for adding
 in to a struct/class and Typedef is a very simple struct making use of
 it to implement a library typedef.

 Unfortunately, Typedef is rather lacking as far as being a typedef is
 concerned, but that's not due to problems with Proxy.

Ok, I see. -- /Jacob Carlborg
Aug 30 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/30/13 11:20 AM, Namespace wrote:
 Typedef was useful not for poking around new type with same properties
 - new name of existing type, but for non-trivial default value:

 typedef int myint = 1;

 void main()
 {
     myint my;
     assert(my is 1);
 }

 Alias does not provide this feature, so D hadn't become better with
 this depreciation (actually the opposite). Nor it had with delete
 operator depreciation for the replacement of destroy, which like in
 case with typedef, does not cover full old feature functionality (and
 functionality what destroy() does provide is useless in many cases). I
 consider both depreciations as mistakes.

Thanks for explanation. I agree that the deprecation of typedef and delete is/was a mistake, and IMO the deprecation of scope and the library fix scoped is the same mistake.

* typedef: it was so ill defined, bringing it any closer to sanity would've broken someone's code. * delete: a festering dung of unsafety straight in the middle of the language. If there's enough argument that the functionality of delete is actually desirable we can always add a function for that. * scope: cute and dangerous in equal proportions - great for a movie character, terrible for language design. Andrei
Aug 30 2013
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/30/2013 11:43 PM, H. S. Teoh wrote:
* scope: cute and dangerous in equal proportions - great for a movie
character, terrible for language design.


What's so terrible about it?

I guess it is about the type system not (yet?) being expressive enough to make sure scoped class instances are not escaped.
Aug 30 2013
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-31 11:46, Andrej Mitrovic wrote:

 So 3 years later, and it's still an issue. I don't even see how Issue
 4636 can even be fixed, there's no way for a template in another
 module to get private access to the class constructor.

A delegate/function pointer will bypass the protection. -- /Jacob Carlborg
Aug 31 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-31 14:03, Andrej Mitrovic wrote:

 How will you pass a pointer to a constructor?

No, you invoke the constructor via a pointer. https://github.com/D-Programming-Language/phobos/blob/master/std/conv.d#L4391 Replace that line with: auto dg = &result.__ctor; dg(args); The static-if needs to be adjusted as well. -- /Jacob Carlborg
Aug 31 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-08-31 20:28, Andrej Mitrovic wrote:

 Hmm... I hope this can actually work when there are multiple ctors,
 how would the compiler know which of the ctors dg should be assigned
 to?

Apparently you cannot use "auto" when having an overloaded symbol, so just use an explicit type: T delegate (Args) dg = &result.__ctor; dg(args); -- /Jacob Carlborg
Aug 31 2013
parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
On 31.08.2013 21:52, Andrej Mitrovic wrote:
 On 8/31/13, Jacob Carlborg <doob me.com> wrote:
 T delegate (Args) dg = &result.__ctor;
 dg(args);

Ah, pretty cool workaround.

Then, why not close that old bug?
Aug 31 2013
parent Piotr Szturmaj <bncrbme jadamspam.pl> writes:
On 31.08.2013 22:33, Andrej Mitrovic wrote:
 On 8/31/13, Piotr Szturmaj <bncrbme jadamspam.pl> wrote:
 On 31.08.2013 21:52, Andrej Mitrovic wrote:
 On 8/31/13, Jacob Carlborg <doob me.com> wrote:
 T delegate (Args) dg = &result.__ctor;
 dg(args);

Ah, pretty cool workaround.

Then, why not close that old bug?

It needs to be fixed first.

I thought you were discussing about and found a fix.
Sep 01 2013
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/30/13 9:55 PM, Maxim Fomin wrote:
 On Friday, 30 August 2013 at 21:11:32 UTC, Andrei Alexandrescu wrote:
 * typedef: it was so ill defined, bringing it any closer to sanity
 would've broken someone's code.

I haven't heard about any specific troubles with typedef which are reason to depreciate the feature.

The problem is nobody could agree whether typedef was a supertype or a subtype of its original type. It was just a bizarre teratoma grown by happenstance.
 In addition to typedef some other
 features are also experiencing troubles (shared, ref, properties,
 invariants,..) yet they are not deprecated.

That's not an argument.
 * delete: a festering dung of unsafety straight in the middle of the
 language.

It was useful to delete class objects at the time where programmer knew that he can delete safely to mitigate the problem of dangling references upon class finalization (by invoking dtor when objects are alive). Right now there is no way to do that. By the way, currently dmd accepts putting safe attribute on class dtor definitions which access GC objects - this is a hole in safety (accessing such elements is not a sufficient reason to be hole in safity, but not reseting pointers to null is).

That would be a bug.
 If there's enough argument that the functionality of delete is
 actually desirable we can always add a function for that.

Probably yes.
 * scope: cute and dangerous in equal proportions - great for a movie
 character, terrible for language design.


 Andrei

I cannot remember any feature implemented in phobos that was better then built-in language construct, including scope. Hasn't C++ followed the same policy and at the end it was considered as mistake?

Not sure I understand.
 At least D has
 many built-in features comparing to C++ and this is advertized as an
 advantage.

I don't think that's how I see things. Andrei
Aug 31 2013
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/31/13 2:46 AM, Andrej Mitrovic wrote:
 On 8/30/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 * typedef: it was so ill defined, bringing it any closer to sanity
 would've broken someone's code.

So it had to be properly defined in the spec and implemented. Meanwhile we're fighting with the Phobos Typedef and it has way more problems right now, some of which will likely be unsolvable. Bug reports: http://d.puremagic.com/issues/show_bug.cgi?id=10872 http://d.puremagic.com/issues/show_bug.cgi?id=10871 http://d.puremagic.com/issues/show_bug.cgi?id=10778 http://d.puremagic.com/issues/show_bug.cgi?id=8618 http://d.puremagic.com/issues/show_bug.cgi?id=7777 http://d.puremagic.com/issues/show_bug.cgi?id=7737

Most of these look approachable, and there are workarounds for the others. Granted, the UDT can't be made 100% like the old typedef (and probably that would be bad, too :o)).
 * scope: cute and dangerous in equal proportions - great for a movie
 character, terrible for language design.

scoped() has its own quirks, for example: http://d.puremagic.com/issues/show_bug.cgi?id=4636 http://d.puremagic.com/issues/show_bug.cgi?id=5115 http://d.puremagic.com/issues/show_bug.cgi?id=10921 And this last bug that was filed (10921) was a bug that was known since 2010: http://d.puremagic.com/issues/show_bug.cgi?id=5115#c6 So 3 years later, and it's still an issue. I don't even see how Issue 4636 can even be fixed, there's no way for a template in another module to get private access to the class constructor. --- Anyway, we've deprecated old keywords, and introduced half-implemented library replacements. I don't see how we stand any better today than we did before.

Library issues are a lot easier to deal with than core language issues. Andrei
Aug 31 2013
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/31/13 1:34 PM, Brian Rogoff wrote:
 On Friday, 30 August 2013 at 21:11:32 UTC, Andrei Alexandrescu wrote:
 * scope: cute and dangerous in equal proportions - great for a movie
 character, terrible for language design.

I'll argue for it and others can then destroy. IME, a lot of the value of functional programming (by which I mean, programming with higher order functions) can be had with downward funargs, as found in Pascal (and more recently, Ada), which don't require garbage collection. The D language expresses closures and higher order functions as delegates, and uses the heap to manage these. I had assumed that we could use 'scope' on the delegate being passed to prevent it's heap allocation, as discussed here: http://stackoverflow.com/questions/4711309/meaning-of-scope-in-d-for-a-parameter

That use will stay and is useful and uncontested. Andrei
Aug 31 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 8/31/13 2:57 PM, Brian Rogoff wrote:
 On Saturday, 31 August 2013 at 21:30:40 UTC, Andrei Alexandrescu wrote:

 http://stackoverflow.com/questions/4711309/meaning-of-scope-in-d-for-a-parameter

That use will stay and is useful and uncontested. Andrei

Sure, but my complaint is that that useful style is cramped by the inability to have scope on *local variables*.

Oh I see. Yes, if we do find a way to define scope to provide guarantees, that would be awesome. Andrei
Aug 31 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-09-01 01:09, Andrei Alexandrescu wrote:

 Oh I see. Yes, if we do find a way to define scope to provide
 guarantees, that would be awesome.

How about this: Object bar () { scope a = new Object; return a; } void main () { bar(); } In the above code the compiler can determine that it's unsafe to return "a", with the following error: Error: escaping reference to scope local a So apparently it can do some form of limited escape analysis. But it's quite easy to fool the compiler. The example below won't result in any compiler errors. Object foo (Object o) { return o; } Object bar () { scope a = new Object; return foo(a); } void main () { bar(); } What if we can help the compiler a bit. If we want to pass a scope object to another function it's required that the function takes the parameter as "scope", like this: Object foo (scope Object o) { return o; } Object bar () { scope a = new Object; return foo(a); } void main () { bar(); } Now the compiler knows the "o" in "foo" is scope allocated and will perform the same analysis it did in the first example. This time the error will appear in "foo" and not in "bar". Perhaps we can allow returning scope allocated objects as well if the function is marked as such: scope(Object) foo (scope Object o) { return o; } Object bar () { scope a = new Object; return foo(a); } void main () { bar(); } Now the error will appear again in "bar" since it doesn't return a scope object. -- /Jacob Carlborg
Sep 01 2013
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Aug 30, 2013 at 02:11:32PM -0700, Andrei Alexandrescu wrote:
 On 8/30/13 11:20 AM, Namespace wrote:
Typedef was useful not for poking around new type with same
properties - new name of existing type, but for non-trivial default
value:

typedef int myint = 1;

void main()
{
    myint my;
    assert(my is 1);
}

Alias does not provide this feature, so D hadn't become better with
this depreciation (actually the opposite). Nor it had with delete
operator depreciation for the replacement of destroy, which like in
case with typedef, does not cover full old feature functionality
(and functionality what destroy() does provide is useless in many
cases). I consider both depreciations as mistakes.

Thanks for explanation. I agree that the deprecation of typedef and delete is/was a mistake, and IMO the deprecation of scope and the library fix scoped is the same mistake.

* typedef: it was so ill defined, bringing it any closer to sanity would've broken someone's code.

Hmm. Is default values the only use case for typedefs? If so, we can already do it without: struct myint { int _impl = 1; alias _impl this; } myint x; assert(x==1); Works on git HEAD.
 * delete: a festering dung of unsafety straight in the middle of the
 language. If there's enough argument that the functionality of delete
 is actually desirable we can always add a function for that.

Well, there's destroy, but last I heard, destroy has some pretty nasty issues on its own... (Not that I'd know anything about it, though, haven't needed to use it. Fortunately.)
 * scope: cute and dangerous in equal proportions - great for a movie
 character, terrible for language design.

What's so terrible about it? And what other solution(s) are there? I only just found out about the library 'scoped' today, and apparently it isn't having a good time either. T -- PNP = Plug 'N' Pray
Aug 30 2013
prev sibling next sibling parent Artur Skawina <art.08.09 gmail.com> writes:
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
Aug 29 2013
prev sibling next sibling parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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 .... ?
Aug 29 2013
prev sibling next sibling parent "Ramon" <spam thanks.no> writes:
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
Aug 29 2013
prev sibling next sibling parent reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
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.
Aug 29 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Aug 30, 2013 at 08:20:13PM +0200, Namespace wrote:
Typedef was useful not for poking around new type with same
properties - new name of existing type, but for non-trivial
default value:

typedef int myint = 1;

void main()
{
	myint my;
	assert(my is 1);
}

Alias does not provide this feature, so D hadn't become better
with this depreciation (actually the opposite). Nor it had with
delete operator depreciation for the replacement of destroy, which
like in case with typedef, does not cover full old feature
functionality (and functionality what destroy() does provide is
useless in many cases). I consider both depreciations as mistakes.

Thanks for explanation. I agree that the deprecation of typedef and delete is/was a mistake, and IMO the deprecation of scope and the library fix scoped is the same mistake.

What, scope is deprecated now? When did this happen, and how come I didn't hear about it? T -- Bare foot: (n.) A device for locating thumb tacks on the floor.
Aug 30 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
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).
Aug 30 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Friday, 30 August 2013 at 11:50:20 UTC, Jacob Carlborg wrote:
 On 2013-08-30 12:56, John Colvin wrote:

 alias this, but without implicit conversion. It just 
 implements all the
 op**** functions (including opDispatch).

So what's the difference to Typedef, declared just below?

Typedef uses Proxy to do the work. Proxy is a mixing template for adding in to a struct/class and Typedef is a very simple struct making use of it to implement a library typedef. Unfortunately, Typedef is rather lacking as far as being a typedef is concerned, but that's not due to problems with Proxy.
Aug 30 2013
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Friday, 30 August 2013 at 12:18:21 UTC, John Colvin wrote:
 On Friday, 30 August 2013 at 11:50:20 UTC, Jacob Carlborg wrote:
 On 2013-08-30 12:56, John Colvin wrote:

 alias this, but without implicit conversion. It just 
 implements all the
 op**** functions (including opDispatch).

So what's the difference to Typedef, declared just below?

Typedef uses Proxy to do the work. Proxy is a mixing template for adding in to a struct/class and Typedef is a very simple struct making use of it to implement a library typedef. Unfortunately, Typedef is rather lacking as far as being a typedef is concerned, but that's not due to problems with Proxy.

What was the initial reason, to move typedef from the language into the library? I assume, that there is something special about typedef that can not be done with alias. If so, why was it moved? It seems to be a trend, to move incomplete features from the language into the library (See also scope -> scoped). I do not like that. :/
Aug 30 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Friday, 30 August 2013 at 14:51:40 UTC, Namespace wrote:
 On Friday, 30 August 2013 at 12:18:21 UTC, John Colvin wrote:
 On Friday, 30 August 2013 at 11:50:20 UTC, Jacob Carlborg 
 wrote:
 On 2013-08-30 12:56, John Colvin wrote:

 alias this, but without implicit conversion. It just 
 implements all the
 op**** functions (including opDispatch).

So what's the difference to Typedef, declared just below?

Typedef uses Proxy to do the work. Proxy is a mixing template for adding in to a struct/class and Typedef is a very simple struct making use of it to implement a library typedef. Unfortunately, Typedef is rather lacking as far as being a typedef is concerned, but that's not due to problems with Proxy.

What was the initial reason, to move typedef from the language into the library? I assume, that there is something special about typedef that can not be done with alias. If so, why was it moved? It seems to be a trend, to move incomplete features from the language into the library (See also scope -> scoped). I do not like that. :/

I believe typedef was deprecated due to no-one being able to agree on the semantics (although it's a bit before my time). I think it needs to come back as a very simple concept: A type duplication. e.g. typedef int myInt; (or typedef myInt = int;) creates a new type that is exactly identical to int, but is a distinct type in the type system. It should provide *exactly* identical behaviour to e.g. having two structs with identical contents but different names. E.g. struct A{} typedef A tA; alias A aA; alias tA atA; typedef aA taA; assert(!is(tA == A)); assert(is(aA == A)); assert(is(atA == tA)); assert(!is(taA == atA)); etc.... It's a really basic feature that D ought to have.
Aug 30 2013
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 I believe typedef was deprecated due to no-one being able to 
 agree on the semantics (although it's a bit before my time). I 
 think it needs to come back as a very simple concept:

 A type duplication. e.g. typedef int myInt; (or typedef myInt = 
 int;) creates a new type that is exactly identical to int, but 
 is a distinct type in the type system.

 It should provide *exactly* identical behaviour to e.g. having 
 two structs with identical contents but different names. E.g.

 struct A{}

 typedef A tA;
 alias A aA;
 alias tA atA;
 typedef aA taA;

 assert(!is(tA == A));
 assert(is(aA == A));
 assert(is(atA == tA));
 assert(!is(taA == atA));
 etc....

 It's a really basic feature that D ought to have.

I don't understand the difference to alias myInt = int; Isn't it the same?
Aug 30 2013
prev sibling next sibling parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On 30/08/13 17:41, Namespace wrote:
 I don't understand the difference to alias myInt = int;
 Isn't it the same?

alias means it's a different name for the same type -- typedef should mean it's a different type with the same properties.
Aug 30 2013
prev sibling next sibling parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Friday, 30 August 2013 at 15:41:55 UTC, Namespace wrote:
 I believe typedef was deprecated due to no-one being able to 
 agree on the semantics (although it's a bit before my time). I 
 think it needs to come back as a very simple concept:

 A type duplication. e.g. typedef int myInt; (or typedef myInt 
 = int;) creates a new type that is exactly identical to int, 
 but is a distinct type in the type system.

 It should provide *exactly* identical behaviour to e.g. having 
 two structs with identical contents but different names. E.g.

 struct A{}

 typedef A tA;
 alias A aA;
 alias tA atA;
 typedef aA taA;

 assert(!is(tA == A));
 assert(is(aA == A));
 assert(is(atA == tA));
 assert(!is(taA == atA));
 etc....

 It's a really basic feature that D ought to have.

I don't understand the difference to alias myInt = int; Isn't it the same?

see the first 2 asserts. alias doesn't create a new type, it just creates a new identifier for the old type. A rough analogy: alias is by reference and typedef is by value
Aug 30 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Friday, 30 August 2013 at 15:41:55 UTC, Namespace wrote:
 I believe typedef was deprecated due to no-one being able to 
 agree on the semantics (although it's a bit before my time). I 
 think it needs to come back as a very simple concept:

 A type duplication. e.g. typedef int myInt; (or typedef myInt 
 = int;) creates a new type that is exactly identical to int, 
 but is a distinct type in the type system.

 It should provide *exactly* identical behaviour to e.g. having 
 two structs with identical contents but different names. E.g.

 struct A{}

 typedef A tA;
 alias A aA;
 alias tA atA;
 typedef aA taA;

 assert(!is(tA == A));
 assert(is(aA == A));
 assert(is(atA == tA));
 assert(!is(taA == atA));
 etc....

 It's a really basic feature that D ought to have.

I don't understand the difference to alias myInt = int; Isn't it the same?

Typedef was useful not for poking around new type with same properties - new name of existing type, but for non-trivial default value: typedef int myint = 1; void main() { myint my; assert(my is 1); } Alias does not provide this feature, so D hadn't become better with this depreciation (actually the opposite). Nor it had with delete operator depreciation for the replacement of destroy, which like in case with typedef, does not cover full old feature functionality (and functionality what destroy() does provide is useless in many cases). I consider both depreciations as mistakes.
Aug 30 2013
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 Typedef was useful not for poking around new type with same 
 properties - new name of existing type, but for non-trivial 
 default value:

 typedef int myint = 1;

 void main()
 {
 	myint my;
 	assert(my is 1);
 }

 Alias does not provide this feature, so D hadn't become better 
 with this depreciation (actually the opposite). Nor it had with 
 delete operator depreciation for the replacement of destroy, 
 which like in case with typedef, does not cover full old 
 feature functionality (and functionality what destroy() does 
 provide is useless in many cases). I consider both 
 depreciations as mistakes.

Thanks for explanation. I agree that the deprecation of typedef and delete is/was a mistake, and IMO the deprecation of scope and the library fix scoped is the same mistake.
Aug 30 2013
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Friday, 30 August 2013 at 21:11:32 UTC, Andrei Alexandrescu 
wrote:
 * typedef: it was so ill defined, bringing it any closer to 
 sanity would've broken someone's code.

I haven't heard about any specific troubles with typedef which are reason to depreciate the feature. In addition to typedef some other features are also experiencing troubles (shared, ref, properties, invariants,..) yet they are not deprecated.
 * delete: a festering dung of unsafety straight in the middle 
 of the language.

It was useful to delete class objects at the time where programmer knew that he can delete safely to mitigate the problem of dangling references upon class finalization (by invoking dtor when objects are alive). Right now there is no way to do that. By the way, currently dmd accepts putting safe attribute on class dtor definitions which access GC objects - this is a hole in safety (accessing such elements is not a sufficient reason to be hole in safity, but not reseting pointers to null is).
 If there's enough argument that the functionality of delete is 
 actually desirable we can always add a function for that.

Probably yes.
 * scope: cute and dangerous in equal proportions - great for a 
 movie character, terrible for language design.


 Andrei

I cannot remember any feature implemented in phobos that was better then built-in language construct, including scope. Hasn't C++ followed the same policy and at the end it was considered as mistake? At least D has many built-in features comparing to C++ and this is advertized as an advantage.
Aug 30 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/30/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 * typedef: it was so ill defined, bringing it any closer to sanity
 would've broken someone's code.

So it had to be properly defined in the spec and implemented. Meanwhile we're fighting with the Phobos Typedef and it has way more problems right now, some of which will likely be unsolvable. Bug reports: http://d.puremagic.com/issues/show_bug.cgi?id=10872 http://d.puremagic.com/issues/show_bug.cgi?id=10871 http://d.puremagic.com/issues/show_bug.cgi?id=10778 http://d.puremagic.com/issues/show_bug.cgi?id=8618 http://d.puremagic.com/issues/show_bug.cgi?id=7777 http://d.puremagic.com/issues/show_bug.cgi?id=7737
 * scope: cute and dangerous in equal proportions - great for a movie
 character, terrible for language design.

scoped() has its own quirks, for example: http://d.puremagic.com/issues/show_bug.cgi?id=4636 http://d.puremagic.com/issues/show_bug.cgi?id=5115 http://d.puremagic.com/issues/show_bug.cgi?id=10921 And this last bug that was filed (10921) was a bug that was known since 2010: http://d.puremagic.com/issues/show_bug.cgi?id=5115#c6 So 3 years later, and it's still an issue. I don't even see how Issue 4636 can even be fixed, there's no way for a template in another module to get private access to the class constructor. --- Anyway, we've deprecated old keywords, and introduced half-implemented library replacements. I don't see how we stand any better today than we did before.
Aug 31 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/31/13, Jacob Carlborg <doob me.com> wrote:
 A delegate/function pointer will bypass the protection.

How will you pass a pointer to a constructor?
Aug 31 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/31/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 Library issues are a lot easier to deal with than core language issues.

Not when you have Kenji around!
Aug 31 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/31/13, Jacob Carlborg <doob me.com> wrote:
 Replace that line with:

 auto dg = &result.__ctor;
 dg(args);

Hmm... I hope this can actually work when there are multiple ctors, how would the compiler know which of the ctors dg should be assigned to?
Aug 31 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/31/13, Jacob Carlborg <doob me.com> wrote:
 T delegate (Args) dg = &result.__ctor;
 dg(args);

Ah, pretty cool workaround.
Aug 31 2013
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/31/13, Piotr Szturmaj <bncrbme jadamspam.pl> wrote:
 On 31.08.2013 21:52, Andrej Mitrovic wrote:
 On 8/31/13, Jacob Carlborg <doob me.com> wrote:
 T delegate (Args) dg = &result.__ctor;
 dg(args);

Ah, pretty cool workaround.

Then, why not close that old bug?

It needs to be fixed first.
Aug 31 2013
prev sibling next sibling parent "Brian Rogoff" <brogoff gmail.com> writes:
On Friday, 30 August 2013 at 21:11:32 UTC, Andrei Alexandrescu 
wrote:
 * scope: cute and dangerous in equal proportions - great for a 
 movie character, terrible for language design.

I'll argue for it and others can then destroy. IME, a lot of the value of functional programming (by which I mean, programming with higher order functions) can be had with downward funargs, as found in Pascal (and more recently, Ada), which don't require garbage collection. The D language expresses closures and higher order functions as delegates, and uses the heap to manage these. I had assumed that we could use 'scope' on the delegate being passed to prevent it's heap allocation, as discussed here: http://stackoverflow.com/questions/4711309/meaning-of-scope-in-d-for-a-parameter I wrote a small test case to try this out, in which I construct a 2-D integration function from a 1-D one, and I use Adam Ruppe's nogc code to seg fault when the GC is hit. Without using a 'scope' on a local variable, I'm forced to pile up the anonymous function calls to get the behavior I want. import std.stdio, std.math; // int delegate(int) adder(int a) { return (int b) { return a + b;}; } float integrate(scope float delegate(float x) f, float lo, float hi, size_t n) { float result = 0.0; float dx = (hi - lo) / n; for (size_t i = 0; i < n; i++) { result += f(lo + i * dx) * dx; } return result; } float integrate(scope float delegate(float, float) f, float x0, float x1, float y0, float y1, size_t nX, size_t nY) { scope auto f_y = delegate float(float y) => integrate(delegate(float x) => f(x,y), x0, x1, nX); return integrate(f_y, y0, y1, nY); // I'll have to write it like this to avoid heap allocation without // local variable scope // return integrate((y) => integrate((x) =>f(x,y), x0, x1, nX), y0, y1, nY); } The code to test demonstrates the issue better, as I'm forced to use inline anonymous functions everywhere to avoid the GC calls (unitCircle and unitSphere omitted for brevity) void main() { scope auto funcX_1 = delegate float(float x) => unitCircle(x); float result1 = integrate(funcX_1 /* (x) => unitCircle(x) */, -1.0, 1.0, 500); writefln("integrate(func1, -1.0, 1.0, 500) = %g", result1); scope auto funcXY_1 = delegate float(float x, float y) => unitSphere(x,y); result1 = integrate(funcXY_1 /* (x,y) => unitSphere(x,y) */, -1.0, 1.0, -1.0, 1.0, 100, 100); writefln("integrate(funcXY_1, -1.0, 1.0, -1.0, 1.0, 100, 100) = %g", result1); } Yes, it's a toy example, but I do program with higher order functions a lot in OCaml and I'd probably use them quite a bit in D, especially if I could avoid impacting the GC. If scope on local variables is going away, it would be nice if the compiler could figure out when I'm using delegate args in a 'downward, non escaping' way and not heap allocate. My tests with DMD indicate that without those 'scope' on the local variable the GC does in fact get hit, and with them it does not. -- Brian PS: I don't really use classes much, so I have little to say about scope on objects PPS: If my missive made little sense, https://en.wikipedia.org/wiki/Funarg_problem http://stackoverflow.com/questions/581182/what-are-downward-funargs
Aug 31 2013
prev sibling next sibling parent "Brian Rogoff" <brogoff gmail.com> writes:
On Saturday, 31 August 2013 at 21:30:40 UTC, Andrei Alexandrescu 
wrote:

 http://stackoverflow.com/questions/4711309/meaning-of-scope-in-d-for-a-parameter

That use will stay and is useful and uncontested. Andrei

Sure, but my complaint is that that useful style is cramped by the inability to have scope on *local variables*. I understand that scope on function parameters will stay. If you're saying that scope on local variables for delegates will stay, then disregard all I've written here, I'm happy! :-). If you look at the code I posted, try removing scope from the 2-D integrate and from the local variable declarations in the main() function, and the program hits the GC, with the scopes, they don't. The problem occurs when I create the local variables to name an anonymous function. I tested this with Adam Ruppe's code which halts when the GC is called. As I said, I'm not arguing for scope on objects, but for contrivances which allow me to program easily with downward funargs and not hit the GC. D goes further than most languages in this and it would be a shame if this capability is removed with no substitute. For those who want to test on their systems, here are the trivial unitCircle and unitSphere I omitted. float unitCircle(float x) { float x2 = x * x; if (x2 < 1.0) { return sqrt(1.0 - x2); } else { return 0.0; } } float unitSphere(float x, float y) { float sum = x*x + y*y; if (sum <= 1.0) { return sqrt(1.0 - sum); } else { return 0.0; } } -- Brian
Aug 31 2013
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 1 September 2013 at 09:24:46 UTC, Jacob Carlborg wrote:
 On 2013-09-01 01:09, Andrei Alexandrescu wrote:

 Oh I see. Yes, if we do find a way to define scope to provide
 guarantees, that would be awesome.

How about this: ...

It is pretty much the meaning I have assumed when collaborating on `scope ref` DIP. Only problem here is, again (and again), attribute inference - using scope variables will require annotating most part of Phobos with `scope` function parameter attributes to be usable.
Sep 01 2013
prev sibling parent "Brian Rogoff" <brogoff gmail.com> writes:
On Saturday, 31 August 2013 at 21:57:37 UTC, Brian Rogoff wrote:
 On Saturday, 31 August 2013 at 21:30:40 UTC, Andrei
 That use will stay and is useful and uncontested.


Actually, after a bit more experimentation, I don't object to removing the scope on local variable delegates. Replacing those with a nested function declaration and a local pointer to function seems to work fine with no GCing, at a very slight cost in elegance IMO; I prefer the delegates to using '&' on a function. It is good to see features being removed from D. -- Brian
Sep 02 2013