Skip to content

Commit 43b503c

Browse files
authored
Merge pull request #8 from ehonda/release/3.0.0
This release adds `NullableOption<T>` that explicitly allows `null` values, and adjusts `Option<T>` to disallow and throw on `null` values.
2 parents a84f392 + 86ce9bf commit 43b503c

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

docs/Conversions.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Implicit vs. Explicit Conversions
2+
3+
This document outlines the design decisions regarding implicit and explicit conversions for the `Option<T>` and `NullableOption<T>` types.
4+
5+
## Summary
6+
7+
| Conversion | Type | Status | Rationale |
8+
| :--- | :--- | :--- | :--- |
9+
| `T``Option<T>` | Implicit | ✅ Allowed | Always safe; never throws. |
10+
| `Option<T>``T` | Explicit | ⚠️ Restricted | Unsafe; throws on `None`. |
11+
12+
## Implicit Conversions (`T``Option<T>`)
13+
14+
We allow implicit conversion from a value `T` to an `Option<T>` (or `NullableOption<T>`).
15+
16+
```csharp
17+
Option<int> opt = 42; // Implicitly creates Option.Some(42)
18+
```
19+
20+
### Reasoning
21+
22+
1. **Safety**: Wrapping a value in an Option is a safe operation that will never throw an exception (assuming the constructor validation passes, e.g., non-null for `Option<T>`).
23+
2. **Convenience**: It allows for cleaner syntax when returning values from methods defined to return `Option<T>`.
24+
3. **Precedent**: This mirrors the behavior of `Nullable<T>` in C# (e.g., `int? x = 5;`).
25+
26+
## Explicit Conversions (`Option<T>``T`)
27+
28+
We **require** an explicit cast when converting from an `Option<T>` back to its underlying type `T`.
29+
30+
```csharp
31+
Option<int> opt = Option.None<int>();
32+
33+
// ❌ Compile Error
34+
int value = opt;
35+
36+
// ✅ Compiles, but may throw at runtime
37+
int value = (int)opt;
38+
```
39+
40+
### Rationale for Explicit Requirement
41+
42+
#### 1. Violation of Design Guidelines
43+
44+
Microsoft's [Framework Design Guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/operator-overloads) explicitly state:
45+
46+
> **DO NOT** throw exceptions from implicit casts.
47+
48+
Converting a `None` state to `T` is impossible without providing a default value or throwing an exception. Since the cast operator cannot accept a default value argument, it must throw `InvalidOperationException` when the option is `None`. Therefore, making this conversion implicit would violate standard .NET design patterns.
49+
50+
#### 2. Safety and Intent
51+
52+
The primary purpose of `Option<T>` is to force the developer to handle the "missing value" case.
53+
54+
* **Explicit Cast**: Signals "I know this might fail, but I am asserting a value exists."
55+
* **Implicit Cast**: Hides the potential failure. A simple assignment `T x = option;` looking perfectly safe could crash the application at runtime.
56+
57+
#### 3. Consistency with `Nullable<T>`
58+
59+
`Option<T>` is designed to be a more expressive counterpart to `Nullable<T>`.
60+
61+
* `Nullable<T>` requires an explicit cast to extract the value:
62+
63+
```csharp
64+
int? n = null;
65+
int i = (int)n; // Throws InvalidOperationException
66+
```
67+
68+
* Making `Option<T>` implicit would break the mental model established by `Nullable<T>` and confuse developers expecting standard C# behavior.
69+
70+
#### 4. "Footgun" Prevention
71+
72+
If the conversion were implicit, it would be easy to accidentally pass an `Option<T>` to a method expecting `T`, leading to runtime crashes that are hard to spot during code review.
73+
74+
```csharp
75+
// If implicit conversion were allowed:
76+
public void Process(string input) { ... }
77+
78+
Option<string> maybeName = Option.None<string>();
79+
Process(maybeName); // Looks safe, compiles, but crashes at runtime!
80+
```
81+
82+
By requiring an explicit cast (or better, using `.Or()`, `.OrDefault()`, or `.Match()`), we ensure the developer consciously handles the control flow.

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PackageReadmeFile>README.md</PackageReadmeFile>
1313
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1414
<PackageTags>optional option maybe</PackageTags>
15-
<Version>2.1.0</Version> <!-- Common version for all packages in src -->
15+
<Version>3.0.0</Version> <!-- Common version for all packages in src -->
1616
<PackageReleaseNotes>See package release notes on GitHub: https://github.com/ehonda/Optional/releases/tag/$(PackageId)-v$(Version)</PackageReleaseNotes>
1717
<DefineConstants>JETBRAINS_ANNOTATIONS</DefineConstants>
1818

0 commit comments

Comments
 (0)