1 module examples.safeDivision;
2 
3 import either;
4 import std.traits;
5 import std.math;
6 
7 version(unittest) {
8   import _expect;
9 }
10 
11 bool isNaN(X)(X x) if (!isFloatingPoint!(X)) {
12   return false;
13 }
14 
15 bool isNaN(X)(X x) if (isFloatingPoint!(X)) {
16   return std.math.isNaN(x);
17 }
18 
19 ///
20 auto divideBy(NumericType)(NumericType numerator, NumericType denominator) {
21   enum NumericType zero = 0;
22 
23   return denominator.bind
24     .when!(zero) ("Division by zero!".bindLeft)
25     .when!(isNaN!NumericType) ("Denominator is NaN.")
26     .when((NumericType a) =>
27       numerator.bind
28         .when!(isNaN!NumericType) ("Numerator is NaN.".bindLeft)
29         .when((NumericType b) => b / a)
30     );
31 }
32 
33 /// divideBy with positive integers
34 unittest {
35   auto result = 10.divideBy(5);
36 
37   result.isRight.should.equal(true);
38 
39   result
40     .when((int value) {
41       value.should.equal(2);
42     });
43 }
44 
45 /// divideBy when the denominator is 0
46 unittest {
47   auto result = 10.divideBy(0);
48 
49   result.isLeft.should.equal(true);
50 
51   result
52     .when((string value) {
53       value.should.equal("Division by zero!");
54     });
55 }
56 
57 /// divideBy when the denominator is NaN
58 unittest {
59   double nan;
60 
61   auto result = double(10).divideBy(nan);
62 
63   result.isLeft.should.equal(true);
64 
65   result
66     .when((string value) {
67       value.should.equal("Denominator is NaN.");
68     });
69 }
70 
71 /// divideBy when the Numerator is NaN
72 unittest {
73   double nan;
74 
75   auto result = double.nan.divideBy(3);
76 
77   result.isLeft.should.equal(true);
78 
79   result
80     .when((string value) {
81       value.should.equal("Numerator is NaN.");
82     });
83 }
84 
85 
86 string toString(T)(Either!(string, T) result) {
87   import std.conv;
88 
89   string message;
90 
91   result
92     .when ((string error) { message = "Error: " ~ error; })
93     .when ((T value) { message = value.to!string; });
94 
95   return "\t" ~ message ~ "\n";
96 }
97 
98 version(runExamples):
99 void main() {
100   import std.stdio;
101 
102   writeln("30 / 4 = ");
103   30.divideBy(4)
104     .toString
105     .writeln;
106 
107   writeln("12.2 / 23.2 = ");
108   double(12.2)
109     .divideBy(22.2)
110     .toString
111     .writeln;
112 
113   writeln("12.2 / 0 = ");
114   double(12.2)
115     .divideBy(0)
116     .toString
117     .writeln;
118 
119   writeln("12.2 / nan = ");
120   double(12.2)
121     .divideBy(double.nan)
122     .toString
123     .writeln;
124 
125   writeln("nan / 3 = ");
126   double.nan
127     .divideBy(double(3))
128     .toString
129     .writeln;
130 }