digitalmars.dip.ideas - Named constructors
- JN (50/50) Nov 08 Let's say you want to have an Angle class, which holds radians
- Arafel (22/34) Nov 09 This would interfere with, and can be simulated through, nested classes:
- IchorDev (17/22) Nov 12 1. Why are your examples using C++ syntax? You should provide
- Salih Dincer (3/9) Nov 24 See also the example in https://dlang.org/phobos/std_sumtype.html
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (32/34) Dec 11 I would refactor this into a type-driven design via
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (6/9) Dec 11 1. Note however that when you already have written `Radians` and
Let's say you want to have an Angle class, which holds radians internally but can be initialized with degrees or radians. ```d class Angle { float radians; this(float rad) : radians(rad) { } this(float degs) : radians(degs * (PI / 180.0f)) { } } Angle a = new Angle(90.0f); ``` Won't work, because you can't have two constructors with same args. Of course you can work it around by e.g. wrapping them in some "Degree" and "Radian" struct so that they're separate type and overloads. Next option, static factory methods: ```d class Angle { float radians; // hide constructor so that users use the factory methods private this(float rad) { radians = rad; } static Angle fromRadians(float rad) { return new Angle(rad); } static Angle fromDegrees(float degs) { return new Angle(degs * (PI / 180.0f)); } } Angle a = Angle.fromDegrees(90.0f); ``` This works, but is awkward because suddenly you don't use the new keyword and it's not immediately obvious that these methods are meant to be used for construction of the object. After typing "Angle." an IDE will suggest all static methods on the class which can be a long list. Named constructors to the rescue: ```d class Angle { float radians; this.fromRadians(float rad) : radians(rad) { } this.fromDegrees(float degs) : radians(degs * (PI / 180.0f)) {} } Angle a1 = new Angle.fromRadians(PI / 2.0f); Angle a2 = new Angle.fromDegrees(90.0f); ``` Much clearer for the user, user can type "new Angle." and the IDE would suggest only constructors instead of all of the static methods on the class. Assumption would be that if named constructors exist, there is no implicit argumentless constructor so Angle a3 = new Angle() won't work anymore.
Nov 08
On 9/11/24 1:02, JN wrote:```d class Angle { float radians; this.fromRadians(float rad) : radians(rad) { } this.fromDegrees(float degs) : radians(degs * (PI / 180.0f)) {} } Angle a1 = new Angle.fromRadians(PI / 2.0f); Angle a2 = new Angle.fromDegrees(90.0f); ```This would interfere with, and can be simulated through, nested classes: ```d enum PI=3.141592f; class Angle { float radians; this(float rad) { radians = rad; } static class FromRadians : Angle { this(float rad) { super(rad); } } static class FromDegrees : Angle { this(float degs) { super(degs * (PI / 180.0f)); } } } void main() { Angle a1 = new Angle.FromRadians(PI / 2.0f); Angle a2 = new Angle.FromDegrees(90.0f); } ``` If you don't want to allow direct instantiations of `Angle`, you can declare it `abstract`, or disable its constructor.
Nov 09
On Saturday, 9 November 2024 at 00:02:52 UTC, JN wrote:Much clearer for the user, user can type "new Angle." and the IDE would suggest only constructors instead of all of the static methods on the class. Assumption would be that if named constructors exist, there is no implicit argumentless constructor so Angle a3 = new Angle() won't work anymore.1. Why are your examples using C++ syntax? You should provide examples that at least compile with *minimal* modification so that people can actually work off of them and test them. Garbage in, garbage out. 2. When your DIP is based on a problem with a feature of an IDE, then the problem lies with your IDE, not the language. The language doesn’t shape itself around IDEs, or else we’d have to discard templates, mixins, and aliases. 3. Is there any reason you can’t just use an external factory function? This is very common practice: ```d Angle angle(float deg) => new Angle(deg*PI/180f); Angle angle(float rad) => new Angle(rad); auto a = angle(deg: 90); auto b = angle(rad: PI/2); ```
Nov 12
On Saturday, 9 November 2024 at 00:02:52 UTC, JN wrote:... Won't work, because you can't have two constructors with same args. Of course you can work it around by e.g. wrapping them in some "Degree" and "Radian" struct so that they're separate type and overloads. ...See also the example in https://dlang.org/phobos/std_sumtype.html SDB 79
Nov 24
On Saturday, 9 November 2024 at 00:02:52 UTC, JN wrote:Let's say you want to have an Angle class, which holds radians internally but can be initialized with degrees or radians.I would refactor this into a type-driven design via ```d struct Radians { private float _value; // various property functions to access `_value`. } struct Degrees { private float _value; // various property functions to access `_value`. } class Angle { float radians; this(Radians rad) : radians(rad) { } this(Degrees degs) : radians(degs * (PI / 180.0f)) { } } ``` . Now you can use ```d Angle(Radians(0.0)); Angle(Degrees(0.0)); ``` or ```d Angle(rad: Radians(0.0)); Angle(deg: Degrees(0.0)); ``` for extra verbosity. This is the beginning of a units of measurements module/package similar to, for instance, https://code.dlang.org/packages/units-d. See also https://en.wikipedia.org/wiki/Unit_of_measurement.
Dec 11
On Thursday, 12 December 2024 at 07:55:14 UTC, Per Nordlöw wrote:On Saturday, 9 November 2024 at 00:02:52 UTC, JN wrote:1. Note however that when you already have written `Radians` and `Degrees` and their mutual conversion there's no need to write `Angle`. 2. Some would argue that it's better to use the singular forms `Radian` and `Degree` instead.Let's say you want to have an Angle class, which holds radians internally but can be initialized with degrees or radians.
Dec 11