Skip to content

Commit

Permalink
doc: Early return doc changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mcmah309 committed Jan 29, 2024
1 parent 69b3a4d commit bd5adda
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 60 deletions.
1 change: 0 additions & 1 deletion lib/option.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
library option;

export 'src/option/future_option.dart';
export 'src/option/future_option_extensions.dart';
export 'src/option/future_result_and_option_extensions.dart';
export 'src/option/option.dart';
Expand Down
1 change: 0 additions & 1 deletion lib/result.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
library result;

export 'src/result/execute_protected.dart';
export 'src/result/future_result.dart';
export 'src/result/record_to_result_extensions.dart';
export 'src/result/result.dart';
export 'src/result/result_extensions.dart';
6 changes: 6 additions & 0 deletions lib/src/option/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ expect(earlyReturn(2), const None());
```
This is a powerful concept and make you code much more concise without losing any safety.

For async, use `Option.async` e.g.
```dart
FutureOption<double> earlyReturn() => Option.async(($) async {
...
});
```

### To Option or Not To Option
If Dart already supports nullable types, why use an
Expand Down
14 changes: 10 additions & 4 deletions lib/src/option/future_option.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import 'dart:async';

import 'package:rust_core/option.dart';
import 'package:rust_core/result.dart';
part of 'option.dart';

/// {@macro futureOption}
typedef FutureOption<T extends Object> = Future<Option<T>>;
Expand Down Expand Up @@ -110,7 +107,16 @@ extension FutureOptionExtension<T extends Object> on FutureOption<T> {
return then((option) => option.zipWith(other, f));
}

//************************************************************************//

Future<T?> toNullable() {
return then((option) => option.toNullable());
}

//************************************************************************//

// ignore: library_private_types_in_public_api
Future<T> operator [](_OptionEarlyReturnKey op) {
return then((value) => value[op]);
}
}
26 changes: 11 additions & 15 deletions lib/src/option/option.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'dart:async';

import 'package:rust_core/result.dart';
import 'package:rust_core/panic.dart';

part 'future_option.dart';

sealed class Option<T extends Object> {
/// Creates a context for early return, similar to "Do notation". Works like the Rust "?" operator, which is a
/// "Early Return Operator". Here "$" is used as the "Early Return Key". when "$" is used on a type [None],
Expand Down Expand Up @@ -28,13 +32,13 @@ sealed class Option<T extends Object> {
/// "Early Return Operator". Here "$" is used as the "Early Return Key". when "$" is used on a type [None],
/// immediately the context that "$" belongs to is returned with None(). e.g.
/// ```
/// Option<int> intNone() => const None();
///
/// Option<int> earlyReturn(int val) => Option(($){
/// int x = intNone()[$]; // returns [None] immediately
/// return Some(val + 3);
/// });
/// expect(earlyReturn(2), const None());
/// FutureOption<int> intNone() async => const None();
///
/// FutureOption<int> earlyReturn(int val) => Option.async(($) async {
/// int x = await intNone()[$]; // returns [None] immediately
/// return Some(x + 3);
/// });
/// expect(await earlyReturn(2), const None());
///```
/// This should be used at the top level of a function as above. Passing "$" to any other functions, nesting, or
/// attempting to bring "$" out of the original scope should be avoided.
Expand Down Expand Up @@ -478,14 +482,6 @@ typedef _OptionEarlyReturnFunction<T extends Object> = Option<T> Function(
typedef _OptionAsyncEarlyReturnFunction<T extends Object> = Future<Option<T>> Function(
_OptionEarlyReturnKey);

extension AsyncOptionEarlyReturnExtension<T extends Object>
on Future<Option<T>> {
// ignore: library_private_types_in_public_api
Future<T> operator [](_OptionEarlyReturnKey op) {
return then((value) => value[op]);
}
}

//************************************************************************//

const none = None();
9 changes: 8 additions & 1 deletion lib/src/result/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,21 @@ Result<int, String> earlyReturn() => Result(($) { // Early Return Key
int y = 2;
// The function will return here will the Err value;
int x = innerErrFn()[$];
return Ok(x.unwrap() + y);
return Ok(x + y);
});
expect(earlyReturn().unwrapErr(), "message");
```
Using the Early Return Key Notation reduces the need for pattern matching or checking, in a safe way. This is quite a
powerful tool. See [here](#pattern-matching-vs-early-return-key) for another example.

For async, use the `Result.async` e.g.
```dart
FutureResult<int, String> earlyReturn() => Result.async(($) async {
...
});
```

## How to Never Unwrap Incorrectly
In Rust, as here, it is possible to unwrap values that should not be unwrapped:
```dart
Expand Down
11 changes: 8 additions & 3 deletions lib/src/result/future_result.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import 'dart:async';

import 'package:rust_core/result.dart';
part of 'result.dart';

/// {@macro futureResult}
typedef FutureResult<S, F extends Object> = Future<Result<S, F>>;
Expand Down Expand Up @@ -167,4 +165,11 @@ extension FutureResultExtension<S, F extends Object> on FutureResult<S, F> {
FutureResult<S, F> copy() {
return then((result) => result.copy());
}

//************************************************************************//

// ignore: library_private_types_in_public_api
Future<S> operator [](_ResultEarlyReturnKey<F> op) {
return then((value) => value[op]);
}
}
64 changes: 29 additions & 35 deletions lib/src/result/result.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'dart:async';

import 'package:rust_core/typedefs.dart';
import 'package:rust_core/panic.dart';

part 'future_result.dart';

/// {@template result}
/// [Result] class representing the type union between [Ok] and [Err].
///
Expand All @@ -10,18 +14,22 @@ import 'package:rust_core/panic.dart';
/// .org/std/result/enum.Result.html
/// {@endtemplate}
sealed class Result<S, F extends Object> {
/// Creates a context for early return, similar to "Do notation". Works like the Rust "?" operator, which is a
/// "Early Return Operator". Here "$" is used as the "Early Return Key". when "$" is used on a type [Err],
/// immediately the context that "$" belongs to is returned with that [Err]. e.g.
/// ```
/// Result<int,String> innerFn(){
/// return Err("message");
/// }

/// Creates a context for early return, similar to "Do notation".
/// Here "$" is used as the "Early Return Key". when "$" is used on a type [Err],
/// immediately the context that "$" belongs to is returned with that [Err].
/// Works like the Rust "?" operator, which is a "Early Return Operator".
/// e.g.
/// ```dart
/// Result<int,String> innerFn() => Err("message");
///
/// Result<int, String> innerFn2() => Ok(1);
///
/// Result<int, String> earlyReturn() => Result(($) {
/// int y = 2;
/// // the function will stop here since an Err was returned
/// int x = innerFn()[$];
/// return Ok(x + y);
/// int x = innerFn()[$]; // returns [Err] here immediately
/// int z = innerFn2()[$]; // innerFn2 will not be executed
/// return Ok(x + y + z);
/// });
/// }
/// expect(earlyReturn().unwrapErr(), "message");
Expand All @@ -36,36 +44,29 @@ sealed class Result<S, F extends Object> {
}
}

/// Similar to [Result()], but works in an asynchronous context.
///
/// Creates a context for early return, similar to "Do notation". Works like the Rust "?" operator, which is a
/// "Early Return Operator". Here "$" is used as the "Early Return Key". when "$" is used on a type [Err],
/// immediately the context that "$" belongs to is returned with that [Err]. e.g.
/// Creates a async context for early return, similar to "Do notation".
/// Here "$" is used as the "Early Return Key". when "$" is used on a type [Err],
/// immediately the context that "$" belongs to is returned with that [Err].
/// Works like the Rust "?" operator, which is a "Early Return Operator".
/// e.g.
///
/// ```dart
/// FutureResult<int,String> innerFn() async {
/// return Err("message");
/// }
/// FutureResult<int,String> innerFn() async => Err("message");
///
/// FutureResult<int, String> innerFn2() async {
/// return Ok(1);
/// }
/// FutureResult<int, String> innerFn2() async => Ok(1);
///
/// FutureResult<int, String> earlyReturn() => Result.earlyAsync(($) async {
/// FutureResult<int, String> earlyReturn() => Result.early(($) async {
/// int y = 2;
/// // the function will stop here since an Err was returned
/// int x = await innerFn()[$];
/// // innerFn2 will not be executed
/// int z = await innerFn2()[$];
/// int x = await innerFn()[$]; // returns [Err] here immediately
/// int z = await innerFn2()[$]; // innerFn2 will not be executed
/// return Ok(x + y + z);
/// });
/// }
/// expect(await earlyReturn().unwrapErr(), "message");
///```
///
/// This should be used at the top level of a function as above. Passing "$" to any other functions, nesting, or
/// attempting to bring "$" out of the original scope should be avoided. Also, using the method [catchError] on the futures after
/// the `[$]` operator might lead to unexpected behaviour.
/// attempting to bring "$" out of the original scope should be avoided.
static Future<Result<S, F>> async<S, F extends Object>(
// ignore: library_private_types_in_public_api
_AsyncResultEarlyReturnFunction<S, F> fn,
Expand Down Expand Up @@ -643,13 +644,6 @@ typedef _ResultEarlyReturnFunction<S, F extends Object> = Result<S, F> Function(
typedef _AsyncResultEarlyReturnFunction<S, F extends Object>
= Future<Result<S, F>> Function(_ResultEarlyReturnKey<F>);

extension AsyncResultEarlyReturnExtension<S, F extends Object>
on Future<Result<S, F>> {
Future<S> operator [](_ResultEarlyReturnKey<F> op) {
return then((value) => value[op]);
}
}

//************************************************************************//

const okay = Ok(unit);
Expand Down

0 comments on commit bd5adda

Please sign in to comment.