Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/avdosev/either_dart

Either class for Dart or Flutter projects.
https://github.com/avdosev/either_dart

dart dart-either dart-either-monad dart-functional-programming either either-dart error-handling flutter functional-programming

Last synced: about 1 month ago
JSON representation

Either class for Dart or Flutter projects.

Awesome Lists containing this project

README

        

# either dart · ![build status](https://github.com/avdosev/either_dart/workflows/unittests/badge.svg)

The library for error handling and railway oriented programming.

This library supports async "map" and async "then" hiding the boilerplate of working with asynchronous computations Future\[Either\].

## Installation

Add to pubspec.yml:

```
dependencies:
either_dart: ... // latest package version
```

## Documentation

https://pub.dev/documentation/either_dart/latest/either/either-library.html

## Pub dev

https://pub.dev/packages/either_dart

## How to use it?

Sections:
* [Basic usage](#basic-usage)
* [Advanced usage](#advanced-usage)
* [Case - Solution](#case---solution)

### Basic usage

Create two entities for example, you can use your own abstractions for your project.

```dart
enum AppError {
NotFound,
// some errors codes
}

class MyError {
final AppError key;
final String? message;

const MyError({
required this.key,
this.message,
});
}
```

We can use Either as shown below:

```dart
Either getCityNameByCode(int code) {
const cities = {
/// some cities
};

if (cities.contains(code)) {
return Right(cities[code]!);
} else {
return Left(
key: AppError.NotFound,
message: '[getCityNameByCode] can`t convert code:$code to city name',
);
}
}
```

Too, you can use `Either.cond` and `Either.condLazy` for simple cases:

```dart
return Either.condLazy(cities.contains(code),
() => cities[code]!,
() => MyError(
key: AppError.NotFound,
message: '[getCityNameByCode] can`t convert code:$code to city name',
),
);
```

Either has the following methods:

***note:*** \
L - current `Left` type \
TL - new generic `Left` type \
R - current `Right` type \
TR - new generic `Right` type

| name | result | description |
| --- | --- | --- |
| `isLeft` | `bool` | Represents the left side of Either class which by convention is a "Failure". |
| `isRight` | `bool` | Represents the right side of Either class which by convention is a "Success" |
| `left` | `L` | Get Left value, may throw an exception when the value is Right. **read-only** |
| `right` | `R` | Get Right value, may throw an exception when the value is Left. **read-only** |
| `either(TL fnL(L left), TR fnR(R right))` | `Either` | Transform values of Left and Right, equal of `bimap` in fp-libraries
| `fold(T fnL(L left), T fnR(R right))` | `T` | Fold Left and Right into the value of one type
| `map(TR fnR(R right))` | `Either` | Transform value of Right
| `mapLeft(TL fnL(L left))` | `Either` | Transform value of Left
| `mapAsync(FutureOr fnR(R right))` | `Future>` | Transform value of Right
| `mapLeftAsync(FutureOr fnL(L left))` | `Future>` | Transform value of Left
| `swap()` | `Either` | Swap Left and Right
| `then(Either fnR(R right))` | `Either` | Transform value of Right when transformation may be finished with an error
| `thenLeft(Either fnL(L left))` | `Either` | Transform value of Left when transformation may be finished with an Right
| `thenAsync(FutureOr> fnR(R right))` | `Future>` | Transform value of Right when transformation may be finished with an error
| `thenLeftAsync(FutureOr> fnL(L left))` | `Future>` | Transform value of Left when transformation may be finished with an Right

### Advanced usage

This library provides an `FutureEither` extension which is designed to handle asynchronous computation with ease.

You don't need to import or use new classes to use it - just use `Future>`

| name | result | description |
| --- | --- | --- |
`either(TL fnL(L left), TR fnR(R right))` | `Future>` | Transform values of Left and Right
`fold(T fnL(L left), T fnR(R right))` | `Future` | Fold Left and Right into the value of one type
`mapRight(FutureOr fnR(R right))` | `Future>` | Transform value of Right
`mapLeft(FutureOr fnL(L left))` | `Future>` | Transform value of Left
`swap()` | `Future>` | Swap Left and Right
`thenRight(FutureOr> fnR(R right))` | `Future>` | Async transform value of Right when transformation may be finished with an error
`thenLeft(FutureOr> fnL(L left))` | `Future>` | Async transform value of Left when transformation may be finished with an Right

Example:

```dart
/// --- helpers ---

import 'package:either_dart/either.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/foundation.dart';
import 'dart:convert';

Future> safe(Future request) async {
try {
return Right(await request);
} catch (e) {
return Left(MyError(
key: AppError.BadRequest,
message: "Request executing with errors:$e"));
}
}

Either checkHttpStatus(http.Response response) {
if (response.statusCode == 200)
return Right(response);
if (response.statusCode >= 500)
return Left(MyError(
key: AppError.ServerError,
message: "Server error with http status ${response.statusCode}"));
return Left(MyError(
key: AppError.BadResponse,
message: "Bad http status ${response.statusCode}"));
}

Future> parseJson(http.Response response) async {
try {

return Right(await compute((body) {
final json = JsonCodec();
return json.decode(body));
}, response.body);
} catch (e) {
return Left(MyError(
key: AppError.JsonParsing,
message: 'failed on json parsing'));
}
}

/// --- app code ---

//// network
Future> getDataFromServer() {
return
safe(http.get(Uri('some uri')))
.thenRight(checkHttpStatus)
.thenRight(parseJson)
.mapRight(Data.fromJson)
}

```

### Case - Solution

* How I can use the value of `Either`?

You can use right or left getters, but you should check what value is stored inside (`isLeft` or `isRight`)

Also, my favorite methods `fold`, `either`

* `fold` - used when you need to transform two rails to one type
* `either` - used for two situations: 1. when you need transform left and right. 2. when you need to use stored value without next usage (see example).

Example:
```dart
/// either method
showNotification(Either value) {
return value.either(
(left) => showWarning(left.message ?? left.key.toString()),
(right) => showInfo(right.toString()),
);
/// equal
if (value.isLeft) {
final left = value.left;
showWarning(left.message ?? left.key.toString()
} else {
showInfo(value.right.toString())
}
}
```
```dart
/// fold method
class MyWidget {
final Either> value;

const MyWidget(this.value);

Widget build(BuildContext context) {
return Text(
value.fold(
(left) => left.message,
(right) => right.join(', ')),
);
/// or
return value.fold(
(left) => _buildSomeErrorWidget(context, left),
(right) => _buildSomeRightWidget(context, right),
);
}
}
```