digitalmars.dip.ideas - Named constructors
- JN (50/50) Nov 08 2024 Let's say you want to have an Angle class, which holds radians
- Arafel (22/34) Nov 09 2024 This would interfere with, and can be simulated through, nested classes:
- IchorDev (17/22) Nov 12 2024 1. Why are your examples using C++ syntax? You should provide
- Quirin Schroll (15/22) Jan 15 Function definitions like that don’t work. Parameter names are
- Salih Dincer (3/9) Nov 24 2024 See also the example in https://dlang.org/phobos/std_sumtype.html
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (32/34) Dec 11 2024 I would refactor this into a type-driven design via
- Per =?UTF-8?B?Tm9yZGzDtnc=?= (6/9) Dec 11 2024 1. Note however that when you already have written `Radians` and
- Jin (11/15) Jan 06 Just use static methods:
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 2024
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 2024
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 2024
On Tuesday, 12 November 2024 at 21:13:18 UTC, IchorDev wrote: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); ```Function definitions like that don’t work. Parameter names are not part of the signature, even if they can participate in overload resolution. You need something like this: ```d enum Deg { init } enum Rad { init } Angle angle(Deg = Deg.init, float deg) => new Angle(deg*PI/180f); Angle angle(Rad = Rad.init, float rad) => new Angle(rad); ``` Then, the signatures differ by the type(!) of the first parameter. Providing a parameter name in a call disambiguates overload resolution, and in fact makes using `deg:` or `rad:` a requirement.
Jan 15
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 2024
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 2024
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 2024
On Saturday, 9 November 2024 at 00:02:52 UTC, JN wrote:```d Angle a1 = new Angle.fromRadians(PI / 2.0f); Angle a2 = new Angle.fromDegrees(90.0f); ```Just use static methods: ```d Angle a1 = Angle.fromRadians(PI / 2.0f); Angle a2 = Angle.fromDegrees(90.0f); ``` Or right unit types: ```d Angle a1 = Angle(PI.rad / 2.0f); Angle a2 = Angle(90.0f.deg); ```
Jan 06