Last active
February 8, 2024 23:28
-
-
Save CristhianLara1996/089863d3f5be1c8879d0f0ac138096d0 to your computer and use it in GitHub Desktop.
Functional programming (Minimalistal)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:equatable/equatable.dart'; | |
// | |
// Copyright (c) 2024 Laraveloper | |
// | |
//////////////// Result.dart | |
/// Result is a type that represents either success (`Ok`) or failure (`Err`). | |
/// | |
/// `Result<T, E>` is the type used for returning and propagating errors. It is | |
/// an object with an `Ok` value, representing success and containing a value, | |
/// and `Err`, representing error and containing an error value. | |
sealed class Result<T extends Object, E extends Object> extends Equatable { | |
const Result._(); | |
/// Create an `Ok` result with the given value. | |
const factory Result.ok(T s) = Ok; | |
/// Create an `Err` result with the given error. | |
const factory Result.err(E err) = Err; | |
/// Call the `catching` function and produce a `Result`. | |
/// | |
/// If the function throws an error, it will be caught and contained in the | |
/// returned result. Otherwise, the result of the function will be contained | |
/// as the `Ok` value. | |
factory Result.of(T Function() catching) { | |
try { | |
return Ok(catching()); | |
} on E catch (e) { | |
return Err(e); | |
} | |
} | |
/// Returns `true` if the option is a `Ok` value. | |
bool isOk(); | |
/// Returns `true` if the option is a `Err` value. | |
bool isErr(); | |
/// Invoke either the `ok` or the `err` function based on the result. | |
/// | |
/// This is a combination of the [map()] and [mapErr()] functions. | |
Result<U, F> fold<U extends Object, F extends Object>( | |
U Function(T) ok, | |
F Function(E) err, | |
); | |
/// Invokes either `ok` or `err` depending on the result. | |
/// | |
/// Identical to [match] except that the arguments are named. | |
R when<R>({required R Function(T) ok, required R Function(E) err}); | |
/// Asynchronously invokes either `ok` or `err` depending on the result. | |
/// | |
/// Identical to [match] except that the arguments are named. | |
Future<R> whenAsync<R>({ | |
required Future<R> Function(T) ok, | |
required Future<R> Function(E) err, | |
}); | |
/// Converts the `Result` into an `Option` containing the value, if any. | |
/// Otherwise returns `None` if the result is an error. | |
Option<T> ok(); | |
/// Converts the `Result` into an `Option` containing the error, if any. | |
/// Otherwise returns `None` if the result is a value. | |
Option<E> err(); | |
/// Returns `res` if the result is an `Err`, otherwise returns `this`. | |
Result<T, F> or<F extends Object>(Result<T, F> res); | |
/// Calls `op` with the `Err` value if the result is `Err`, otherwise returns | |
/// `this`. | |
Result<T, F> orElse<F extends Object>(Result<T, F> Function(E) op); | |
} | |
/// An `Ok<T, E>` is a `Result` that represents the successful value. | |
/// | |
/// You can create an `Ok` using either the `Ok()` constructor or the | |
/// `Result.ok()` factory constructor. | |
class Ok<T extends Object, E extends Object> extends Result<T, E> { | |
/// Create an `Ok` result with the given value. | |
const Ok(T s) | |
: _ok = s, | |
super._(); | |
final T _ok; | |
/// Wrapped value. | |
T get value => _ok; | |
@override | |
List<Object?> get props => [_ok]; | |
@override | |
bool get stringify => true; | |
@override | |
bool isOk() => true; | |
@override | |
bool isErr() => false; | |
@override | |
R when<R>({required R Function(T) ok, required R Function(E) err}) => ok(_ok); | |
@override | |
Future<R> whenAsync<R>({ | |
required Future<R> Function(T) ok, | |
required Future<R> Function(E) err, | |
}) => | |
ok(_ok); | |
@override | |
Result<U, F> fold<U extends Object, F extends Object>( | |
U Function(T) ok, | |
F Function(E) err, | |
) => | |
Ok(ok(_ok)); | |
@override | |
Option<T> ok() => Some<T>(_ok); | |
@override | |
Option<E> err() => None<E>(); | |
@override | |
Result<T, F> or<F extends Object>(Result<T, F> res) => Ok(_ok); | |
@override | |
Result<T, F> orElse<F extends Object>(Result<T, F> Function(E) op) => Ok(_ok); | |
/// Returns a new [Ok]<[Unit], E> | |
// static Ok<Unit, E> unit<E extends Object>() => Ok<Unit, E>(Unit.unit); | |
} | |
/// An `Err<T, E>` is a `Result` that represents a failure. | |
/// | |
/// You can create an `Err` using either the `Err(E)` constructor or the | |
/// `Result.err(E)` factory constructor. | |
class Err<T extends Object, E extends Object> extends Result<T, E> { | |
/// Create an `Err` result with the given error. | |
const Err(E err) | |
: _err = err, | |
super._(); | |
final E _err; | |
/// Wrapped error. | |
E get error => _err; | |
@override | |
List<Object?> get props => [_err]; | |
@override | |
bool get stringify => true; | |
@override | |
bool isOk() => false; | |
@override | |
bool isErr() => true; | |
@override | |
R when<R>({required R Function(T) ok, required R Function(E) err}) => | |
err(_err); | |
@override | |
Result<U, F> fold<U extends Object, F extends Object>( | |
U Function(T) ok, | |
F Function(E) err, | |
) => | |
Err(err(_err)); | |
@override | |
Option<T> ok() => None<T>(); | |
@override | |
Option<E> err() => Some<E>(_err); | |
@override | |
Result<T, F> or<F extends Object>(Result<T, F> res) => res; | |
@override | |
Result<T, F> orElse<F extends Object>(Result<T, F> Function(E) op) => | |
op(_err); | |
@override | |
Future<R> whenAsync<R>({ | |
required Future<R> Function(T) ok, | |
required Future<R> Function(E) err, | |
}) => | |
err(_err); | |
} | |
/// [OptionBase] is the base for [Option] class. | |
/// It must not be used directly. | |
abstract class OptionBase<T extends Object> extends Equatable { | |
const OptionBase._(); | |
@override | |
bool? get stringify => true; | |
/// Returns an nullable that represents this optional value. | |
/// | |
/// If Option has Some value, it will return that value. | |
/// If Option has a None value, it will return `null`. | |
T? toNullable(); | |
/// Returns `true` if the option is a `Some` value. | |
bool isSome() => this is Some; | |
/// Returns `true` if the option is a `None` value. | |
bool isNone() => this is None; | |
} | |
//////////////// OPTION.dart | |
sealed class Option<T extends Object> extends OptionBase<T> { | |
// with | |
// OptionUnwrapMixin<T>, | |
// OptionMatchMixin<T>, | |
// OptionMapFilterMixin<T>, | |
// OptionLogicMixin<T>, | |
// OptionToResultMixin<T> { | |
/// Create a [Some] option with the given value. | |
const factory Option.some(T v) = Some; | |
/// Create a [None] option with no value. | |
const factory Option.none() = None; | |
const Option._() : super._(); | |
/// Create a option from a nullable value. | |
/// | |
/// Passing a non-null value will result in a [Some]. | |
/// Passing a `null` value will result in a [None]. | |
factory Option.from(T? v) { | |
return v == null ? None<T>() : Some(v); | |
} | |
} | |
/// Type `Some<T>` is an `Option` that contains a value. | |
/// | |
/// You can construct a `Some` using the `Some()` constructor or by calling the | |
/// `Option.some()` factory constructor. The advantage of using the factory | |
/// constructor on `Option` is that it will yield a `None` if the passed value | |
/// is `null`, which can be helpful. | |
class Some<T extends Object> extends Option<T> { | |
/// Create a `Some` option with the given value. | |
const Some(T v) | |
: _some = v, | |
super._(); | |
final T _some; | |
/// Wrapped value. | |
T get some => _some; | |
@override | |
List<Object?> get props => [_some]; | |
@override | |
T? toNullable() => _some; | |
} | |
/// Type `None<T>` is an `Option` that does not contain any value. | |
/// | |
/// You can construct a `None` using the `None()` constructor or by calling the | |
/// `Option.none()` factory constructor. A `None` is also returned when a `null` | |
/// is passed to the `Option.some()` factory constructor. | |
class None<T extends Object> extends Option<T> { | |
/// Create a `None` option with no value. | |
const None() : super._(); | |
@override | |
List<Object> get props => []; | |
@override | |
T? toNullable() => null; | |
} | |
////// Implement | |
Result<String, Exception> testString(String name) { | |
return Result.of(() { | |
return 'Hello $name'; | |
}); | |
} | |
void main(List<String> args) { | |
print(testString('Laraveloper').when(ok:(r)=> r,err:(e)=> e)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment