Result vs Either (fpdart)
Either funksional proqramlaşdırmadan gəlir və Flutter cəmiyyətindəki "clean architecture" şablonlarında geniş yayılıb. Dart-da onun ən çox işlədilən implementasiyası fpdart paketidir.
Qaydası konvensiyadır: `Left` — uğursuzluq, `Right` — uğur. Yəni Either<Failure, List<Order>> "ya Failure, ya sifariş siyahısı" deməkdir.
Struktur baxımından Either ilə Result eyni ideyadır: iki halı bir tipdə birləşdirmək və çağıranı hər ikisini nəzərə almağa məcbur etmək. Fərq detallarındadır:
ResultFlutter-in rəsmi dizayn pattern-idir, layihənin öz faylında yaşayır (10-15 sətir), asılılıq tələb etmir və Dart-ın özswitch-i ilə açılır.Eitherpaket gətirir, əvəzində zəngin kompozisiya API-si verir:map,mapLeft,flatMap,getOrElse,match,tryCatch. fpdart həm dəOption,Task,TaskEitherkimi əlaqəli tiplər gətirir —TaskEither"uğursuz ola bilən asinxron əməliyyat" deməkdir.
Seçim texniki üstünlük məsələsi deyil, komanda və layihə məsələsidir.
// ══════ RESULT (rəsmi pattern) ══════
abstract interface class OrderRepository {
Future<Result<List<Order>>> fetchMine();
}
class OrderRepositoryRemote implements OrderRepository {
@override
Future<Result<List<Order>>> fetchMine() async {
try {
final dtos = await _api.getOrders();
return Result.ok(dtos.map((d) => d.toDomain()).toList());
} on SocketException {
return const Result.error(NetworkFailure());
}
}
}
// Notifier-də:
state = switch (await _repository.fetchMine()) {
Ok(:final value) => OrdersState.loaded(value),
Error(:final error) => OrdersState.failed(error),
};
// ══════ EITHER (fpdart) ══════
import 'package:fpdart/fpdart.dart';
abstract interface class OrderRepository {
// Left = Failure, Right = uğur. Sıra konvensiyadır.
Future<Either<Failure, List<Order>>> fetchMine();
}
class OrderRepositoryRemote implements OrderRepository {
@override
Future<Either<Failure, List<Order>>> fetchMine() async {
try {
final dtos = await _api.getOrders();
// `Either.of` Right (uğur) yaradır.
return Either.of(dtos.map((d) => d.toDomain()).toList());
} on SocketException {
return Either.left(const NetworkFailure());
}
}
}
// Notifier-də, variant 1 — fpdart-ın `match`-i:
state = (await _repository.fetchMine()).match(
(failure) => OrdersState.failed(failure),
(orders) => OrdersState.loaded(orders),
);
// Notifier-də, variant 2 — Dart-ın öz pattern-ləri:
state = switch (await _repository.fetchMine()) {
Left(value: final failure) => OrdersState.failed(failure),
Right(value: final orders) => OrdersState.loaded(orders),
};
// ══════ EITHER-in əsl gücü: kompozisiya ══════
// Ardıcıl əməliyyatlar `flatMap` ilə zəncirlənir; ilk uğursuzluqda
// zəncir dayanır və `Left` dəyəri sona qədər daşınır.
Future<Either<Failure, Receipt>> checkout(Cart cart) async {
final reserved = await _inventory.reserve(cart);
return reserved.flatMap((reservation) {
// Bu blok yalnız `reserved` Right olduqda işləyir.
return _pricing
.quote(reservation)
.map((quote) => Receipt(reservation: reservation, quote: quote));
});
}
// Müqayisə üçün: `mapLeft` xətanı çevirir, dəyərə toxunmur.
final Either<String, List<Order>> forLogging =
(await _repository.fetchMine()).mapLeft((f) => f.runtimeType.toString());Eyni repository iki yanaşma ilə. Diqqət et: fpdart-ın `Either`-i də Dart-ın pattern-ləri ilə açıla bilir.
| Meyar | `Result` (rəsmi) | `Either` (fpdart) |
|---|---|---|
| Asılılıq | Yoxdur — 15 sətirlik öz faylın | `fpdart` paketi |
| Rəsmi mövqe | Flutter sənədlərinin dizayn pattern-i | Rəsmi sənədlərdə yoxdur |
| Oxunaqlıq (yeni komanda üzvü) | `Ok` / `Error` — dərhal aydındır | `Left` / `Right` — konvensiyanı bilmək lazımdır |
| Kompozisiya (zəncirləmə) | Əl ilə: `switch` + erkən `return` | `flatMap`, `map`, `mapLeft` — güclü tərəfi |
| Xəta tipi | `Exception` (rəsmi implementasiyada) | İstənilən tip: `Either<Failure, T>` |
| Əlavə tiplər | Yoxdur | `Option`, `Task`, `TaskEither` və s. |
| Öyrənmə əyrisi | Praktik olaraq yoxdur | FP anlayışları (functor, monad) ilə tanışlıq faydalıdır |
Tövsiyə. Bu branch-ın stack-ində (Riverpod + freezed) Result daha yaxşı uyğun gəlir və səbəblər konkretdir:
1. Rəsmi pattern: Flutter sənədlərində sənədləşdirilib, ona görə komanda mübahisəsi qısa olur.
2. Asılılıq yoxdur: 15 sətirlik fayl. Paket versiyası, breaking change, pub outdated problemi yoxdur.
3. `AsyncValue` ilə üst-üstə düşmə azdır: Riverpod-un AsyncValue-u artıq loading/error/data modelləşdirir; Result yalnız yazma əməliyyatlarında lazım olur. Bunun üçün paket gətirmək bahalı görünür.
4. `switch` kifayətdir: Dart 3-ün pattern-ləri Left/Right üçün də işləyir, ona görə fpdart-ın API-sinin əsas hissəsi (match, fold) artıq dildə var.
Either-in haqlı olduğu hallar da var:
- Komanda FP ilə rahatdır və
flatMapzəncirləri gündəlik dildir. - Çox addımlı, ardıcıl uğursuzluqlar olan axınlar (validasiya → hesablama → yazma) —
flatMaporadaswitchyığınından qat-qat qısadır. - Layihədə artıq fpdart var və
Optionkimi tiplər işlədilir.
Ən pis seçim — ikisini birlikdə işlətmək: bir repository Result, digəri Either qaytarır. O zaman hər yeni fayl üçün "burada hansı?" sualı yaranır və çevirmə funksiyaları yazılır.
Miqrasiya barədə praktik qeyd: Either-dən Result-a (ya da əksinə) keçid mexanikidir və bir dəfə edilməlidir. Ən sadə yol — sərhədləri saymaqla başlamaq: grep -rn "Either<" lib | wc -l. Rəqəm 20-dən azdırsa, keçid bir günlük işdir; 200-dürsə, tədricən (repository-bir-repository) etmək lazımdır və bu müddətdə çevirici funksiya (Either<Failure,T> → Result<T>) saxlanılır.
Praktika. Eyni repository metodunu iki cür yaz: Result və Either (fpdart-ı dev_dependencies-ə əlavə edib sınamaq kifayətdir). Sonra ikisinə də notifier tərəfindən istifadə yaz və özün üçün qərar ver.
Hazır sayılır: hər iki variant kompilyasiya olunur, və ARCHITECTURE.md-də bir sətirlik qərar yazılıb: "layihədə xəta axını üçün X işlədilir, çünki ...".
📚 Mənbələr və sənədlər
- fpdart paketirəsmipub.dev
`Either`, `Option`, `Task`, `TaskEither` və onların API-si — `Either.of`, `Either.left`, `map`, `mapLeft`, `flatMap`, `match`.
- Result obyektləri ilə xəta idarəsirəsmidocs.flutter.dev
Rəsmi alternativ — və onun niyə asılılıq tələb etmədiyi.
- Dart: pattern-lərrəsmidart.dev
`Left(value: final f)` kimi obyekt pattern-ləri — fpdart ilə də işləyir.