Skip to content

Instantly share code, notes, and snippets.

@CristhianLara1996
Last active February 8, 2024 23:28
Show Gist options
  • Save CristhianLara1996/089863d3f5be1c8879d0f0ac138096d0 to your computer and use it in GitHub Desktop.
Save CristhianLara1996/089863d3f5be1c8879d0f0ac138096d0 to your computer and use it in GitHub Desktop.
Functional programming (Minimalistal)
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