|
| 1 | +# LanguageExt v5 Migration Analysis |
| 2 | + |
| 3 | +Analysis of breaking changes when upgrading Dbosoft.Functional from LanguageExt v4 (`[4.4.0,4.5.0)`) to v5 (`5.0.0-beta-77`). |
| 4 | + |
| 5 | +> Target framework also changes: `netstandard2.0` -> `net10.0` (v5 requires .NET 10+) |
| 6 | +
|
| 7 | +--- |
| 8 | + |
| 9 | +## Build Result: 18 errors, 4 warnings |
| 10 | + |
| 11 | +--- |
| 12 | + |
| 13 | +## Breaking Changes in LanguageExt v5 |
| 14 | + |
| 15 | +### 1. Removed Types |
| 16 | + |
| 17 | +| Removed | Replacement | |
| 18 | +|---------|-------------| |
| 19 | +| `EitherAsync<L, R>` | `EitherT<L, IO, R>` (monad transformer over IO) | |
| 20 | +| `OptionAsync<A>` | `OptionT<IO, A>` | |
| 21 | +| `TryAsync<A>` | `TryT<IO, A>` | |
| 22 | +| `TryOption<A>` | `OptionT<Try, A>` | |
| 23 | +| `Aff<A>`, `Aff<RT, A>` | `Eff<A>` (now handles both sync and async) | |
| 24 | +| `NewType<NEWTYPE, A, PRED, ORD>` | C# `record` types (NewType removed entirely) | |
| 25 | +| `Pred<A>`, `True<A>` | Removed (predicate system) | |
| 26 | + |
| 27 | +### 2. Removed Namespaces / Type Classes |
| 28 | + |
| 29 | +| Removed | Replacement | |
| 30 | +|---------|-------------| |
| 31 | +| `LanguageExt.ClassInstances.Pred` | Removed | |
| 32 | +| `LanguageExt.TypeClasses` | `LanguageExt.Traits` (static abstract interface members) | |
| 33 | +| `struct, Ord<A>` constraint | `Ord<A>` is now a trait interface, not a struct type class | |
| 34 | + |
| 35 | +### 3. Removed Prelude Functions |
| 36 | + |
| 37 | +| Removed | Replacement | |
| 38 | +|---------|-------------| |
| 39 | +| `Prelude.RightAsync<L, R>(x)` | `EitherT<L, IO, R>.Right(x)` or `Pure(x)` | |
| 40 | +| `Prelude.LeftAsync<L, R>(x)` | `EitherT<L, IO, R>.Left(x)` or `Fail(x)` | |
| 41 | +| `Prelude.SomeAsync(x)` | `OptionT<IO, A>.Some(x)` | |
| 42 | +| `x.ToAsync()` (on Either/Option) | Wrap in transformer: `EitherT.lift(io)` | |
| 43 | + |
| 44 | +### 4. Effect System Changes |
| 45 | + |
| 46 | +- `Aff<A>` merged into `Eff<A>` (which now supports both sync and async) |
| 47 | +- `Eff<RT, A>` no longer requires `HasCancel<RT>` trait |
| 48 | +- New `IO<A>` monad is the foundation for all side effects |
| 49 | +- `liftIO(async () => ...)` and `liftEff(async () => ...)` for async lifting |
| 50 | + |
| 51 | +### 5. Validation Changes |
| 52 | + |
| 53 | +- `Validation` `|` operator semantics changed (v4 `|` behaved like `&`) |
| 54 | +- `ValidationT<F, M, A>` monad transformer added |
| 55 | + |
| 56 | +### 6. Either `bi` Functions |
| 57 | + |
| 58 | +- Arguments to `BiMap`, `BiFold`, etc. are **flipped** (Left/Right handler order swapped) |
| 59 | + |
| 60 | +### 7. Semigroup / Monoid |
| 61 | + |
| 62 | +- Now traits that types must implement directly (static abstract `operator+` and `Empty`) |
| 63 | +- Cannot make external types into semigroups/monoids anymore |
| 64 | + |
| 65 | +### 8. Transformers Package Removed |
| 66 | + |
| 67 | +- `LanguageExt.Transformers` deleted (500k+ lines of generated `MapT`, `BindT`, etc.) |
| 68 | +- Replaced by generic trait-based methods in `LanguageExt.Core` |
| 69 | + |
| 70 | +--- |
| 71 | + |
| 72 | +## Impact on Dbosoft.Functional Source Files |
| 73 | + |
| 74 | +### `EitherExtensions.cs` — 6 errors |
| 75 | + |
| 76 | +All methods using `EitherAsync<,>` are broken: |
| 77 | + |
| 78 | +| Method | Issue | Migration Path | |
| 79 | +|--------|-------|----------------| |
| 80 | +| `ToEitherRight<TIn>(this TIn)` | Uses `Prelude.RightAsync` (removed) | Return `Either<Error, TIn>` synchronously, or use `EitherT` | |
| 81 | +| `ToEitherLeft<TIn>(this Error)` | Uses `Prelude.LeftAsync` (removed) | Return `Either<Error, TIn>` synchronously, or use `EitherT` | |
| 82 | +| `IfNoneAsync<TIn>(...)` | Uses `.ToAsync()` (removed) | Rewrite using `Either` + `Eff`/`IO` for async | |
| 83 | +| `NoneToError<T>(this EitherAsync<Error, Option<T>>, Error)` | `EitherAsync` removed | Rewrite for `EitherT<Error, IO, Option<T>>` or `Eff<Either<Error, T>>` | |
| 84 | +| `SomeToError<T>(...)` (both overloads) | `EitherAsync` removed | Same as NoneToError | |
| 85 | + |
| 86 | +### `AffExtensions.cs` — 3 errors |
| 87 | + |
| 88 | +| Method | Issue | Migration Path | |
| 89 | +|--------|-------|----------------| |
| 90 | +| `ToAff<R>(this EitherAsync<Error, R>)` | Both `Aff` and `EitherAsync` removed | **Delete entirely** — no direct equivalent needed | |
| 91 | +| `ToEitherAsync<R>(this ValueTask<Fin<R>>)` | `EitherAsync` removed | Convert to `Either<Error, R>` from `Fin<R>` (sync), or wrap in `IO`/`Eff` | |
| 92 | + |
| 93 | +### `UseExtensions.cs` — 4 errors |
| 94 | + |
| 95 | +| Method | Issue | Migration Path | |
| 96 | +|--------|-------|----------------| |
| 97 | +| `Use<L,R1,R2>(this EitherAsync<L,R1>, ...)` | `EitherAsync` removed | Rewrite for `EitherT<L, IO, R1>` or drop in favor of `Eff` resource tracking | |
| 98 | +| `Use<L,R1,R2>(this Task<Either<L,R1>>, ...)` | Still compiles (uses `Task<Either>`) | **Keep** — may still work | |
| 99 | +| `Use<L,R1,R2>(this Either<L,R1>, ...)` | Still compiles | **Keep** | |
| 100 | + |
| 101 | +> Note: v5 `Eff<A>` has built-in resource tracking with automatic cleanup, which may make `Use` extensions redundant. |
| 102 | +
|
| 103 | +### `ValidatingNewType.cs` — 5 errors |
| 104 | + |
| 105 | +| Issue | Detail | |
| 106 | +|-------|--------| |
| 107 | +| `LanguageExt.ClassInstances.Pred` removed | Entire predicate system gone | |
| 108 | +| `LanguageExt.TypeClasses` removed | Replaced by `LanguageExt.Traits` | |
| 109 | +| `NewType<NEWTYPE, A, True<A>, ORD>` removed | `NewType` with 4 generic args no longer exists | |
| 110 | +| `True<A>` removed | Predicate class gone | |
| 111 | +| `struct, Ord<A>` constraint invalid | `Ord<A>` is now a trait, not a struct | |
| 112 | + |
| 113 | +**Migration:** `NewType` is replaced by C# `record` types in v5. The entire `ValidatingNewType` class needs a ground-up redesign — likely as an abstract `record` with a static `Validate` method returning `Validation<Error, NEWTYPE>`. |
| 114 | + |
| 115 | +### `ErrorExtensions.cs` — 0 errors |
| 116 | + |
| 117 | +`Error` type changed from struct to abstract record, but the `Print` method's pattern matching on `ManyErrors` / `Exceptional` should still work since those subtypes exist in v5. **Likely still compiles** but needs verification. |
| 118 | + |
| 119 | +--- |
| 120 | + |
| 121 | +## Impact on Dbosoft.Functional.Json |
| 122 | + |
| 123 | +### `NewTypeJsonConverter.cs` — Multiple errors expected |
| 124 | + |
| 125 | +The entire converter depends on `NewType<NEWTYPE, A, PRED, ORD>` which no longer exists in v5. Also uses: |
| 126 | +- `Pred<A>` (removed) |
| 127 | +- `Ord<A>` as struct constraint (changed to trait) |
| 128 | +- `NewType<,,,>` reflection checks |
| 129 | + |
| 130 | +**Migration:** Needs complete rewrite. Since v5 replaces `NewType` with plain C# `record` types, the JSON converter concept needs to be reimagined — possibly as a converter for record types that implement a specific interface, or it may become unnecessary if records use standard JSON serialization. |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +## Impact on Tests |
| 135 | + |
| 136 | +### `FluentAssertions.LanguageExt` — Incompatible |
| 137 | + |
| 138 | +Package `0.5.0` targets LanguageExt v4. Will not work with v5 types. Need to either: |
| 139 | +- Find/create a v5-compatible assertion library |
| 140 | +- Write custom assertion extensions (like the `AwesomeAssertions.LanguageExt` in SAP2Dynamics) |
| 141 | + |
| 142 | +### Test code using `EitherAsync`, `RightAsync`, `LeftAsync`, `Aff` |
| 143 | + |
| 144 | +All test methods using these types/functions need rewriting. |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## Decision Points |
| 149 | + |
| 150 | +1. **`EitherAsync` replacement strategy**: Use `EitherT<L, IO, R>` everywhere, or switch to `Eff<Either<L, R>>`/`Eff<R>` with error handling? |
| 151 | +2. **`ValidatingNewType` redesign**: Keep the concept with records, or drop it in favor of simple record + static validation? |
| 152 | +3. **`NewTypeJsonConverter` redesign**: Rewrite for records, or drop if no longer needed? |
| 153 | +4. **`UseExtensions` for `EitherAsync`**: Drop the `EitherAsync` overload, or rewrite for `EitherT`? (v5 `Eff` has built-in resource tracking) |
| 154 | +5. **`AffExtensions`**: Delete entirely since both `Aff` and `EitherAsync` are gone? |
| 155 | +6. **Test assertion library**: Write custom v5 assertions or wait for community package? |
0 commit comments