digitalmars.D - User-defined "opIs"
- Meta (39/39) Sep 24 2014 The following code fails under DMD 2.065:
- Adam D. Ruppe (9/11) Sep 24 2014 It is not supposed to work - the docs don't list is as
- Meta (18/31) Sep 24 2014 I'm thinking more for structs, which can't be null. The only
- ketmar via Digitalmars-d (8/10) Sep 24 2014 i don't think that ER is needed. `is` is non-overloadable by design.
- Steven Schveighoffer (7/44) Sep 25 2014 You're looking at it the wrong way. 'is' is not overridable, and
- Marco Leise (32/32) Sep 27 2014 I'm against overloading identity checking, too. Instead I
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (3/33) Sep 28 2014 Maybe allow this only for types that somehow implicitly convert
- Marco Leise (17/23) Sep 28 2014 That sounds like a messy rule set on top of the original when
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (5/27) Sep 28 2014 Yeah, it wasn't a good idea. Somehow it felt strange to throw all
- Marco Leise (38/42) Sep 29 2014 I have the same feeling. No matter how we do it, one of the
The following code fails under DMD 2.065: struct Test { bool opBinary(string op: "is", T: typeof(null))(T val) { return false; } bool opBinaryRight(string op: "is", T: typeof(null))(T val) { return false; } } void main() { auto t = Test(); //Error: incompatible types for ((t) is (null)): 'Test' and 'typeof(null)' //assert(t !is null); //assert(null !is t); } Is this supposed to work, and if not, should an enhancement be made to allow it? This is stopping std.typecons.Proxy from being a true proxy. See the following: struct Test { int* ptr; mixin Proxy!ptr; this(int* p) { ptr = p; } } auto i = new int; assert(i !is null); auto t = Test(i); //Error: incompatible types for ((t) !is (null)): 'Test' and 'typeof(null)' //assert(t !is null);
Sep 24 2014
On Wednesday, 24 September 2014 at 22:48:36 UTC, Meta wrote:Is this supposed to work, and if not, should an enhancement be made to allow it?It is not supposed to work - the docs don't list is as overridable (http://dlang.org/expression.html look for "Identity expressions") - and I don't think it should work either. Consider: class A { opIs() } if(a is null) If that were rewritten into if(a.opIs(null))... and a were null, you'd have some trouble. Putting in an automatic null pre-check would start to complicate that is currently a simple operator.
Sep 24 2014
On Wednesday, 24 September 2014 at 23:08:26 UTC, Adam D. Ruppe wrote:On Wednesday, 24 September 2014 at 22:48:36 UTC, Meta wrote:I'm thinking more for structs, which can't be null. The only other option right now is to use alias this, but that won't work in Proxy's case. For classes, I agree it's tricky... I know we're trying to *remove* things from Object, but what if we added: class Object { static bool opIs(T, U)(inout(T) a, inout(U) b) inout { //Some implementation } } And calls to is on an object get rewritten as: Object.opIs!(typeof(a), typeof(b))(a, b) So `if (a is null)` becomes `if (Object.opIs(a, null))` only when a is a class. When it's a struct, it would just be rewritten as `<struct name>.opIs!T(val)`.Is this supposed to work, and if not, should an enhancement be made to allow it?It is not supposed to work - the docs don't list is as overridable (http://dlang.org/expression.html look for "Identity expressions") - and I don't think it should work either. Consider: class A { opIs() } if(a is null) If that were rewritten into if(a.opIs(null))... and a were null, you'd have some trouble. Putting in an automatic null pre-check would start to complicate that is currently a simple operator.
Sep 24 2014
On Wed, 24 Sep 2014 22:48:35 +0000 Meta via Digitalmars-d <digitalmars-d puremagic.com> wrote:Is this supposed to work, and if not, should an enhancement be=20 made to allow it?i don't think that ER is needed. `is` is non-overloadable by design. making it overloadable will bring in the requrements for something like `is_which_is_not_overloadable`. which in turn will be asked to make overloadable, which will bring... ah, and so on. ;-) i see some little problems with Proxy here, though. well, Proxy is not ideal. ;-)
Sep 24 2014
On 9/24/14 6:48 PM, Meta wrote:The following code fails under DMD 2.065: struct Test { bool opBinary(string op: "is", T: typeof(null))(T val) { return false; } bool opBinaryRight(string op: "is", T: typeof(null))(T val) { return false; } } void main() { auto t = Test(); //Error: incompatible types for ((t) is (null)): 'Test' and 'typeof(null)' //assert(t !is null); //assert(null !is t); } Is this supposed to work, and if not, should an enhancement be made to allow it? This is stopping std.typecons.Proxy from being a true proxy. See the following: struct Test { int* ptr; mixin Proxy!ptr; this(int* p) { ptr = p; } } auto i = new int; assert(i !is null); auto t = Test(i); //Error: incompatible types for ((t) !is (null)): 'Test' and 'typeof(null)' //assert(t !is null);You're looking at it the wrong way. 'is' is not overridable, and shouldn't be. But the type of null should be overridable (or you should be able to tell the compiler "this type can be compared with null"). I believe there has been discussion on this in the past, can't remember where it led. -Steve
Sep 25 2014
I'm against overloading identity checking, too. Instead I would propose to change its definition to make Proxy structs work. The rules are currently: For class objects, identity is defined as the object references are for the same object. Null class objects can be compared with is. For struct objects, identity is defined as the bits in the struct being identical. For static and dynamic arrays, identity is defined as referring to the same array elements and the same number of elements. For other operand types, identity is defined as being the same as equality. If we changed that to: A byte for byte comparison of both operands is performed. For reference types this is the reference itself. Breakage is limited to current uses of "is" that clone the behavior of "==", which a deprecation warning could catch. And to comparisons of slices with static arrays. (You'd have to write `dynamicArr is staticArr[]` then.) This is all comprehensible since you need to be educated about the difference between value types and reference types anyways. What it breaks it makes good for by allowing these to compare equal: struct CrcProxy { ubyte[4] value; } CrcProxy crc32a; ubyte[4] crc32b; assert (crc32a is crc32b); // Yes, it is byte identical. If empowering structs to fully emulate built-in types is still on the agenda it might be the way of least resistance. -- Marco
Sep 27 2014
On Saturday, 27 September 2014 at 11:38:51 UTC, Marco Leise wrote:I'm against overloading identity checking, too. Instead I would propose to change its definition to make Proxy structs work. The rules are currently: For class objects, identity is defined as the object references are for the same object. Null class objects can be compared with is. For struct objects, identity is defined as the bits in the struct being identical. For static and dynamic arrays, identity is defined as referring to the same array elements and the same number of elements. For other operand types, identity is defined as being the same as equality. If we changed that to: A byte for byte comparison of both operands is performed. For reference types this is the reference itself.Maybe allow this only for types that somehow implicitly convert to each other, i.e. via alias this?Breakage is limited to current uses of "is" that clone the behavior of "==", which a deprecation warning could catch. And to comparisons of slices with static arrays. (You'd have to write `dynamicArr is staticArr[]` then.) This is all comprehensible since you need to be educated about the difference between value types and reference types anyways. What it breaks it makes good for by allowing these to compare equal: struct CrcProxy { ubyte[4] value; } CrcProxy crc32a; ubyte[4] crc32b; assert (crc32a is crc32b); // Yes, it is byte identical. If empowering structs to fully emulate built-in types is still on the agenda it might be the way of least resistance.
Sep 28 2014
Am Sun, 28 Sep 2014 10:44:47 +0000 schrieb "Marc Sch=C3=BCtz" <schuetzm gmx.net>:On Saturday, 27 September 2014 at 11:38:51 UTC, Marco Leise wrote:That sounds like a messy rule set on top of the original when alias this does not represent all of the type. You have a size_t and a struct with a pointer that aliases itself to some size_t that can be retrieved through that pointer. The alias this will make it implicitly convert to size_t and the byte for byte comparison will happily compare two equally sized varibles (size_t and pointer). So how narrow would the rule have to be defined before it reads: If you compare with a struct that consists only of one member that the struct aliases itself with, a variable of the type of that member will be compared byte for byte with the struct. --=20 MarcoA byte for byte comparison of both operands is performed. For reference types this is the reference itself.=20 Maybe allow this only for types that somehow implicitly convert=20 to each other, i.e. via alias this?
Sep 28 2014
On Sunday, 28 September 2014 at 14:27:56 UTC, Marco Leise wrote:Am Sun, 28 Sep 2014 10:44:47 +0000 schrieb "Marc Schütz" <schuetzm gmx.net>:Yeah, it wasn't a good idea. Somehow it felt strange to throw all type safety out of the window, but on the other hand, bit-level comparison is the purpose of `is`, which isn't typesafe to begin with.On Saturday, 27 September 2014 at 11:38:51 UTC, Marco Leise wrote:That sounds like a messy rule set on top of the original when alias this does not represent all of the type. You have a size_t and a struct with a pointer that aliases itself to some size_t that can be retrieved through that pointer. The alias this will make it implicitly convert to size_t and the byte for byte comparison will happily compare two equally sized varibles (size_t and pointer). So how narrow would the rule have to be defined before it reads: If you compare with a struct that consists only of one member that the struct aliases itself with, a variable of the type of that member will be compared byte for byte with the struct.A byte for byte comparison of both operands is performed. For reference types this is the reference itself.Maybe allow this only for types that somehow implicitly convert to each other, i.e. via alias this?
Sep 28 2014
Am Sun, 28 Sep 2014 14:43:03 +0000 schrieb "Marc Sch=C3=BCtz" <schuetzm gmx.net>:Yeah, it wasn't a good idea. Somehow it felt strange to throw all=20 type safety out of the window, but on the other hand, bit-level=20 comparison is the purpose of `is`, which isn't typesafe to begin=20 with.I have the same feeling. No matter how we do it, one of the comparisons will always be left behind: 1a) identity of references with same referred type - objects with same reference and common inheritance branch - static arrays and the full slice over them - pointers of same type 1b) reference identity after implicit casts per language rules - void* with reference type - void[] with typed array 2) raw bit level equality (includes 1a) and parts of 1b)) - proxy struct with wrapped type - signed with unsigned types on the bit level 3a) logical / overridable equality for user defined types - comparison of uint.max with -1 results in false - safe type widening 1.0f with 1.0, -1 with -1L, char types - opEquals() 3b) 3a) with "C-style" equality for built-in types - equal after integer type promotion - equal after implicit cast One can define these from other perspectives, and the bit-wise struct comparison may be seen as a default opEquals (which I do here) or as a raw bit equality. "=3D=3D" is doing 3b). "is" comprises 1a), 1b) and 3b) as a fallback to be defined for all types. Defining "is" as 2) would remove the overlap for both operators in 3b), but loose type-safety in 1a+b) and disallow comparison of `void[] is ulong[]` since their lengths will differ even if they span the same memory area. With two operators we can capture any two subsets which sound logical on their own, but not all of the possible notions of identity and equality. And since people already wonder about "is" in D and "=3D=3D=3D" in JavaScript it is probably not a good idea to add even more comparison operators. So I conclude that we will have to live with that shortcoming. --=20 Marco
Sep 29 2014