digitalmars.D - Supertypes, subtypes, and more
- bearophile (18/41) Jun 25 2014 In Phobos we have Typedef (that needs some improvements), that
- Meta (12/59) Jun 25 2014 You can do this in C# as well:
- bearophile (4/7) Jun 25 2014 But it's essential to do that on structs.
- Meta (2/10) Jun 25 2014 I believe it works for structs as well.
- Joseph Rushton Wakeling via Digitalmars-d (29/32) Jun 25 2014 I'd like to mention that this has an unfortunate issue with respect to
- Tobias Pankrath (19/27) Jun 25 2014 Did you try using a method?
- Joseph Rushton Wakeling via Digitalmars-d (4/23) Jun 25 2014 Yes, someone suggested this to me quite recently in fact. But I think i...
- Justin Whear (6/16) Jun 25 2014 Does this do everything you're looking for?
- Tofu Ninja (4/25) Jun 25 2014 Now if only we could get that to work with more than one type,
- bearophile (23/25) Jul 10 2014 It seems OK. But there is a bit too much boilerplate. So is it
In Phobos we have Typedef (that needs some improvements), that allows to define a not compatible type. In D we have "alias this" that inside a struct allows to create a new type (with instance object of size equal or larger than the original type) with similar semantics of a given type. This allows to define a type that acts "like" another. In Ada there is a standard way to restrict a type. In a Reddit thread I have found this simple example of modern Ada code, a sufficiently common need:My go-to example is social-security numbers, because it's simple enough that people can see what's going on as well as provides a good way to illustrate how subtypes can allow one to 'ignore'1 the implementation. Subtype Social_Security_Number is String(1..11) with Dynamic_Predicate => (for all Index in Social_Security_Number'Range => (case Index is when 4|7 => Social_Security_Number(Index) = '-', when others => Social_Security_Number(Index) in '0'..'9' ) ); -- The above declaration can allows me to skip the validity -- checks of parameters within the body of a subprogram as -- the constraints are checked on subprogram-call. -- I do not need to check the validity of the return value, -- an attempt to return a non-conformant string will raise -- and exception. Function Get_SSN( Record : ID ) return Social_Security_Number; -- Likewise, passing a non-conformant value to SSN will raise -- an exception. Procedure Save_SSN( Record : ID; SSN : Social_Security_Number );I think where you expect a String you can send a Social_Security_Number, but where it expects a Social_Security_Number it doesn't accept a String. (The Dynamic_Predicate and Static_Predicate of modern Ada allow to perform such restrictions.) So I think there are some analogies with OOP. I'd like to do something like this in D, with structs/values. Bye, bearophile
Jun 25 2014
On Wednesday, 25 June 2014 at 15:33:16 UTC, bearophile wrote:In Phobos we have Typedef (that needs some improvements), that allows to define a not compatible type. In D we have "alias this" that inside a struct allows to create a new type (with instance object of size equal or larger than the original type) with similar semantics of a given type. This allows to define a type that acts "like" another. In Ada there is a standard way to restrict a type. In a Reddit thread I have found this simple example of modern Ada code, a sufficiently common need:class SocialSecurityNumber { string _ssn; //Implicitly converts to string public static implicit operator string(SocialSecurityNumber ssn) { return _ssn; } }My go-to example is social-security numbers, because it's simple enough that people can see what's going on as well as provides a good way to illustrate how subtypes can allow one to 'ignore'1 the implementation. Subtype Social_Security_Number is String(1..11) with Dynamic_Predicate => (for all Index in Social_Security_Number'Range => (case Index is when 4|7 => Social_Security_Number(Index) = '-', when others => Social_Security_Number(Index) in '0'..'9' ) ); -- The above declaration can allows me to skip the validity -- checks of parameters within the body of a subprogram as -- the constraints are checked on subprogram-call. -- I do not need to check the validity of the return value, -- an attempt to return a non-conformant string will raise -- and exception. Function Get_SSN( Record : ID ) return Social_Security_Number; -- Likewise, passing a non-conformant value to SSN will raise -- an exception. Procedure Save_SSN( Record : ID; SSN : Social_Security_Number );I think where you expect a String you can send a Social_Security_Number, but where it expects a Social_Security_Number it doesn't accept a String. (The Dynamic_Predicate and Static_Predicate of modern Ada allow to perform such restrictions.) So I think there are some analogies with OOP. I'd like to do something like this in D, with structs/values. Bye, bearophile
Jun 25 2014
Meta:class SocialSecurityNumber {But it's essential to do that on structs. Bye, bearophile
Jun 25 2014
On Wednesday, 25 June 2014 at 19:30:25 UTC, bearophile wrote:Meta:I believe it works for structs as well.class SocialSecurityNumber {But it's essential to do that on structs. Bye, bearophile
Jun 25 2014
On 25/06/14 17:33, bearophile via Digitalmars-d wrote:In D we have "alias this" that inside a struct allows to create a new type (with instance object of size equal or larger than the original type) with similar semantics of a given type. This allows to define a type that acts "like" another.I'd like to mention that this has an unfortunate issue with respect to encapsulation of data. See: http://d.puremagic.com/issues/show_bug.cgi?id=10996 The problem is that if you have a subtype implemented via alias this, struct Foo { SomeOtherStruct payload; alias payload this; // ... other stuff ... } ... then ideally you'd like the payload field to be private, because otherwise you're exposing implementation details, which could be nasty if you ever want to alter the design. (SOMEONE using your code is going to wind up assuming that accessing payload directly is OK.) However, if you try and implement it this way, struct Foo { private SomeOtherStruct payload; public alias payload this; // ... other stuff ... } ... the compiler won't accept it, because the private access to payload clashes with public access via alias this. It's a shame, because the principle -- payload is private, but public access to its fields is available via the alias this -- is a nice one; and it's clearly something that was envisioned for the language (TDPL p. 232, I think). Personally, I'd really appreciate it if the above-mentioned issue could get some compiler dev attention. :-)
Jun 25 2014
struct Foo { private SomeOtherStruct payload; public alias payload this; // ... other stuff ... } ... the compiler won't accept it, because the private access to payload clashes with public access via alias this.Did you try using a method? module1 --- struct Subtype { private int x; public int getX() { return x; } alias getX this; } --- module2 --- import module1; void main() { Subtype st = Subtype(2); int x = st; } --- Works for me.
Jun 25 2014
On 25/06/14 23:10, Tobias Pankrath via Digitalmars-d wrote:Did you try using a method? module1 --- struct Subtype { private int x; public int getX() { return x; } alias getX this; } --- module2 --- import module1; void main() { Subtype st = Subtype(2); int x = st; } --- Works for me.Yes, someone suggested this to me quite recently in fact. But I think it doesn't solve the real issue -- you're still exposing implementation details, as a user can always call getX directly.
Jun 25 2014
On Wed, 25 Jun 2014 15:33:14 +0000, bearophile wrote:I think where you expect a String you can send a Social_Security_Number, but where it expects a Social_Security_Number it doesn't accept a String. (The Dynamic_Predicate and Static_Predicate of modern Ada allow to perform such restrictions.) So I think there are some analogies with OOP. I'd like to do something like this in D, with structs/values. Bye, bearophileDoes this do everything you're looking for? http://dpaste.dzfl.pl/50531f5b1356 The only potential problem I see is that the invariant presumably won't be compiled in when in release mode.
Jun 25 2014
On Wednesday, 25 June 2014 at 20:59:00 UTC, Justin Whear wrote:On Wed, 25 Jun 2014 15:33:14 +0000, bearophile wrote:Now if only we could get that to work with more than one type, such that we could have subtype implicitly convertible to more than one type.I think where you expect a String you can send a Social_Security_Number, but where it expects a Social_Security_Number it doesn't accept a String. (The Dynamic_Predicate and Static_Predicate of modern Ada allow to perform such restrictions.) So I think there are some analogies with OOP. I'd like to do something like this in D, with structs/values. Bye, bearophileDoes this do everything you're looking for? http://dpaste.dzfl.pl/50531f5b1356 The only potential problem I see is that the invariant presumably won't be compiled in when in release mode.
Jun 25 2014
(Sorry for the very late answer, I have had to care for animals). Justin Whear:Does this do everything you're looking for? http://dpaste.dzfl.pl/50531f5b1356e.It seems OK. But there is a bit too much boilerplate. So is it possible to create something for Phobos usable like (note the ctor of this SocialSecurityNumber should copy the attributes of the invariant, so if the given invariant is pure, the ctor should be pure. This invariant is pure safe): mixin(Subtype!("SocialSecurityNumber", q{ import std.algorithm: all; import std.ascii: isDigit; assert(impl_.length == 11); assert(impl_[0 .. 3].all!isDigit); assert(impl_[3] == '-'); assert(impl_[4 .. 6].all!isDigit); assert(impl_[6] == '-'); assert(impl_[7 .. 11].all!isDigit); })); Once issue 8864 is implemented (merging this pull: https://github.com/D-Programming-Language/dmd/pull/3680 ) array literals too will be allowed: SocialSecurityNumber[] numbers = ["123-45-6789", "123-45-6785"]; Bye, bearophile
Jul 10 2014