Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.0.0 #14

Merged
merged 38 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1965517
feat: Change option to a concrete type
mcmah309 Dec 8, 2024
1578120
doc: Update option docs
mcmah309 Dec 8, 2024
d25ae68
feat: Add initial env and fs libraries
mcmah309 Dec 8, 2024
e03be60
feat: Implement more of fs
mcmah309 Dec 8, 2024
cc73d0d
feat: Add panicHandler
mcmah309 Dec 8, 2024
e0f0885
feat: Implement more of fs
mcmah309 Dec 8, 2024
6d6d2a3
feat: Implement more of fs
mcmah309 Dec 8, 2024
98f2ad6
feat: Implement rest of fs static methods
mcmah309 Dec 8, 2024
2f28543
feat: Add static methods to fs for File and OpenOption
mcmah309 Dec 8, 2024
366d93d
test: Add fs tests
mcmah309 Dec 8, 2024
6259718
test: Improve env test
mcmah309 Dec 8, 2024
b7852c0
feat: Replace v and value on option with toNullable()
mcmah309 Dec 8, 2024
9d24105
feat: Update Option
mcmah309 Dec 8, 2024
3479b1c
doc: Update
mcmah309 Dec 9, 2024
d09a682
feat: Cell library update
mcmah309 Dec 9, 2024
470dcd2
feat: Make iter more compatible with null types
mcmah309 Dec 9, 2024
9383a02
feat: Make slice and vec more compatible with null types
mcmah309 Dec 9, 2024
ceacb28
feat: Make path more compatible with null types
mcmah309 Dec 9, 2024
89b1fab
feat: Remove infallible
mcmah309 Dec 9, 2024
9f07c1a
feat: Update cell
mcmah309 Dec 9, 2024
f940944
feat: Make array more compatible with null types
mcmah309 Dec 9, 2024
12cb2db
doc: Option update
mcmah309 Dec 9, 2024
48208e9
doc: Update
mcmah309 Dec 9, 2024
e0d1aea
test: Update
mcmah309 Dec 9, 2024
fa20c1a
doc: Update README
mcmah309 Dec 9, 2024
439b4ea
chore: Bump
mcmah309 Dec 9, 2024
9c26f27
chore: Format
mcmah309 Dec 9, 2024
b284d98
doc: Update
mcmah309 Dec 9, 2024
76d2826
doc: Update
mcmah309 Dec 9, 2024
bc9ee72
feat: Update
mcmah309 Dec 9, 2024
7c3e3ff
feat: Update
mcmah309 Dec 9, 2024
d889ee1
feat: remove okay and error
mcmah309 Dec 11, 2024
54dec45
feat: Add err to result
mcmah309 Dec 11, 2024
3e29916
refactor: Rename e and o to v
mcmah309 Dec 11, 2024
20c7a57
feat: Make peek only work for non-null
mcmah309 Dec 11, 2024
9786a79
chore: Bump
mcmah309 Dec 11, 2024
d303805
chore: Formatting
mcmah309 Dec 11, 2024
5400029
chore: Changelog
mcmah309 Dec 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 3.0.0

- Breaking: New sealed type `Option` implementation
- Breaking: Nullable methods added for every `Option` method and vice versa
- Breaking: Rename okay/o and e/error on Result to v
- Add `Fs` library
- Add `Env` library
- Misc additions
- Rename/refactor

## 2.0.2

- Add `Option.of`
Expand All @@ -8,14 +18,14 @@

## 2.0.0

- Breaking: Add `o`/`okay` to `Ok`, `e`/`error` to `Err`, `value` to `Option`
- Breaking: Change `channel` to `localChannel`
- Breaking: Remove deprecations
- Breaking: Remove individual library import files
- Breaking: Rename extensions
- Breaking: Rename errors
- Add `Path`, `UnixPath`, `WindowsPath`
- Add `KeyedMutex`
- Add `o`/`okay` to `Ok`, `e`/`error` to `Err`, `value` to `Option`
- Change `channel` to `localChannel`
- Remove deprecations
- Remove individual library import files
- Rename extensions
- Rename errors

## 1.3.7

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ See the [Documentation Book 📖](https://mcmah309.github.io/rust) for a deeper
---
> Goal: Get the index of every "!" in a string not followed by a "?"

**[Rust](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=6010cc86519e58e4592247403830cde7):**
**[Rust](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=f8a2979808d21a7bfe22a3cfb70ec389):**
```rust
use std::iter::Peekable;

Expand All @@ -36,7 +36,7 @@ fn main() {
_ => continue,
}
}
assert_eq!(answer, [2, 7]);
println!("{:?}", answer); // [2, 7]
}
```
**Dart:**
Expand All @@ -59,11 +59,11 @@ void main() {
break;
case ["!", _]:
answer.push(index);
case [_, "!"] when iter.peek().isNone():
case [_, "!"] when iter.peek() == null: // or `iter.peekOpt().isNone()`
answer.push(index + 1);
}
}
expect(answer, [2, 7]);
print(answer); // [2, 7]
}
```

Expand Down
2 changes: 2 additions & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
- [array](./libs/array/array.md)
- [cell](./libs/cell/cell.md)
<!-- - [convert](./libs/convert/convert.md) -->
- [env](./libs/env/env.md)
- [fs](./libs/fs/fs.md)
- [iter](./libs/iter/iter.md)
- [ops](./libs/ops/ops.md)
- [option](./libs/option/option.md)
Expand Down
10 changes: 7 additions & 3 deletions book/src/introduction/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@
From a language perspective we believe Dart is sadly lacking in a few areas, of which this package solves:

* Dart utilizes unchecked try/catch exceptions. Handling errors as values is preferred for maintainability, thus the `Result` type.
* Dart has nullable types but you cannot do null or non-null specific operations without a bunch of `if` statements. `Option<T>` fixes this with no runtime cost and you can easily switch back and forth to nullable types since it is just a zero cost extension type of `T?`.
* Dart is missing the functionality of Rust's `?` operator, so we implemented it in Dart.
* Dart has nullable types but you cannot do null or non-null specific operations without a bunch of `if` statements and
bugs may be introduced due to `T??` not being possible. `Option<T>` fixes this with no runtime cost and you can easily switch back and forth.
* Dart is missing the functionality of Rust's early return operator (`?`) operator, so we implemented it in Dart.
* Dart is missing a built in `Cell` type or equivalent (and `OnceCell`/`LazyCell`).
* Dart's `List` type is an array/vector union (it's growable or non-growable). This is not viewable at the type layer, which may lead to runtime exceptions and encourages using growable `List`s everywhere even when you do not need to, which is less performant. So we added `Arr` (array).
* Dart has no concept of a slice type, so allocating sub-lists is the only method, which is not that efficient. So we added `Slice<T>`.
* Dart's between isolate communication is by ports (`ReceivePort`/`SendPort`), which is untyped and horrible, we standardized this with introducing `channel` for typed bi-directional isolate communication.
* Dart's iteration methods are lacking for `Iterable` and `Iterator` (there are none! just `moveNext()` and `current`), while Rust has an abundance of useful methods. So we introduced Rust's `Iterator`.
* Dart's iteration methods are lacking for `Iterable` and `Iterator` (there are none! just `moveNext()` and `current`), while Rust has an abundance of useful methods. So we introduced Rust's `Iterator` as `Iter`.
* Dart does not have built in path manipulation. So, we added `Path`, a zero cost extension type of `String` for path manipulation.
* Dart's `File`/`Directory`/`Link` abstraction was the wrong choice and prone to exceptions. We believe `Fs` and `Path` are a stronger and safer alternative.
* No built in cross-platform environment introspect - `Platform` does not work on web. So we added `Env` which is cross-platform.

## I Know Rust, Can This Package Benefit My Team and I?
***
Expand Down
6 changes: 3 additions & 3 deletions book/src/libs/cell/cell.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ expect(result, const Ok(()));
result = cell.set(20);
expect(result, const Err(20));
```
The base type for all `OnceCell`s is `NullableOnceCell`.
The base type for all `OnceCell`s is `OnceCellNullable`.

## LazyCell
A value which is initialized on the first access.
Expand All @@ -68,7 +68,7 @@ int secondCall = lazyCell();
expect(callCount, equals(1));
expect(secondCall, equals(20));
```
The base type for all `LazyCell`s is `NullableLazyCell`.
The base type for all `LazyCell`s is `LazyCellNullable`.

## LazyCellAsync
A value which is asynchronously initialized on the first access.
Expand All @@ -86,4 +86,4 @@ int secondCall = lazyCell(); // Could also call `await lazyCell.force()` again.
expect(callCount, equals(1));
expect(secondCall, equals(20));
```
The base type for all `LazyCellAsync`s is `NullableLazyCellAsync`.
The base type for all `LazyCellAsync`s is `LazyCellNullableAsync`.
18 changes: 0 additions & 18 deletions book/src/libs/convert/convert.md
Original file line number Diff line number Diff line change
@@ -1,19 +1 @@
# Convert
***
## Infallible

`Infallible` is the error type for errors that can never happen. This can be useful for generic APIs that use Result
and parameterize the error type, to indicate that the result is always Ok. Thus these types expose `intoOk` and
`intoErr`.

```dart

Result<int, Infallible> x = Ok(1);
expect(x.intoOk(), 1);
Result<Infallible, int> w = Err(1);
expect(w.intoErr(), 1);
```

```
typedef Infallible = Never;
```
17 changes: 17 additions & 0 deletions book/src/libs/env/env.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Env

Env introduces `Env` for handling the environment. It works like `Platform`,
except it is cross-platform (also works on web), since it is independent of `dart:io`, and has additional methods.

```dart
void main(){
if(Env.isWeb) {
print("On web, doing nothing.");
}
else if(Env.isLinux || Env.isMacOs) {
Env.currentDirectory = "/";
print("Moved current directory to root");
}
...
}
```
21 changes: 21 additions & 0 deletions book/src/libs/fs/fs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Fs

Fs introduces `Fs`, a container of static methods for working with the file system in a safe manner.
`Fs` combines many of the functionalities in `File`/`Directory`/`Link`/`FileStat`/`FileSystemEntity`
into one location and will never throw an exception. Instead of using instances of the previous
entities, `Fs` works only on paths.

```dart
Result<(), IoError> = await Fs.createDir("path/to/dir".asPath());
// handle
```
rather than
```dart
try {
await Directory("path/to/dir").create();
}
catch (e) {
// handle
}
// handle
```
14 changes: 11 additions & 3 deletions book/src/libs/iter/iter.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ makes working with collections of `rust` types and regular Dart types a breeze.
```dart
List<int> list = [1, 2, 3, 4, 5];
Iter<int> filtered = list.iter().filterMap((e) {
if (e % 2 == 0) {
return e * 2;
}
return null;
});
expect(filtered, [4, 8]);
// or
filtered = list.iter().filterMapOpt((e) {
if (e % 2 == 0) {
return Some(e * 2);
}
Expand All @@ -27,12 +35,12 @@ for (final e in iter.take(5).map((e) => e * e)) {
}
}
expect(collect, [4, 16]);
Option<int> next = iter.next();
expect(next, Some(6));
int? next = iter.next();
expect(next, 6);
collect.add(next.unwrap());
next = iter.next();
collect.add(next.unwrap());
expect(next, Some(7));
expect(next, 7);
while(iter.moveNext()){
collect.add(iter.current * iter.current);
}
Expand Down
4 changes: 2 additions & 2 deletions book/src/libs/ops/ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ They have two uses:
1. `RangeBounds` can be used to get a `Slice` of an `Arr`, `Slice`, or `List`.
```dart
void func(RangeBounds bounds) {
Arr<int> arr = Arr.range(0, 10);
Slice<int> slice = arr(bounds);
List<int> list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
Slice<int> slice = list(bounds);
expect(slice, equals([4, 5, 6, 7, 8, 9]));
}

Expand Down
109 changes: 49 additions & 60 deletions book/src/libs/option/option.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
# Option
***
Option represents the union of two types - `Some<T>` and `None`. An `Option<T>` is an extension type of `T?`. Therefore, `Option`
has zero runtime cost.

rust support nullable and `Option` implementations of classes and methods for ergonomic convenience where possible, but you
can easily switch between the two with no runtime cost.
`Option` represents the union of two types - `Some<T>` and `None`.

`Option` is easy to declare and translate back and forth between nullable types.
```dart
Option<int> option = None;
Option<int> option = Some(1);

int? nullable = option.v; // or `.value`
int? nullable = option.toNullable();
option = Option.of(nullable);

nullable = option as int?; // or
option = nullable as Option<int>; // or
```

### Usage
Expand All @@ -34,7 +29,7 @@ You can also use Option in pattern matching
switch(Some(2)){
case Some(:final v):
// do something
default:
case _:
// do something
}
```
Expand Down Expand Up @@ -66,65 +61,60 @@ FutureOption<double> earlyReturn() => Option.async(($) async {
...
});
```
### Discussion

### To Option or Not To Option
If Dart already supports nullable types, why use an option type? Nullable types may required an
uncomfortable level of null checking and nesting. Even so, one may also still need to write a null
assertion `!` for some edge cases where the compiler is not smart enough.
The `Option` type provides an alternative solution with methods and early return.
#### If Dart Has Nullable Types Why Ever Use `Option`?

Methods:
```dart
final profile;
final preferences;

switch (fetchUserProfile()
.map((e) => "${e.name} - profile")
.andThen((e) => Some(e).zip(fetchUserPreferences()))) {
case Some(:final value):
(profile, preferences) = value;
default:
return;
}
`Option` is wrapper around a value that may or may be set. A nullable type is a type that may or may not be set.
This small distinction leads to some useful differences:

print('Profile: $profile, Preferences: $preferences');
- Any extension method on `T?` also exists for `T`. So null specific extensions cannot be added.
Also since `T` is all types, there would be a lot of clashes with existing types if you tried to
do so - e.g. `map` on `Iterable`. While `Option` plays well for a pipeline style of programming.

```
Early Return Notation:
```dart
final (profile, preferences) = fetchUserProfile()
.map((e) => "${e.name} - profile")
.andThen((e) => Some(e).zip(fetchUserPreferences()))[$];
- `T??` is not possible, while Option<Option<T>> or Option<T?> is. This may be useful,
e.g.

print('Profile: $profile, Preferences: $preferences');
```
Traditional Null-Based Approach:
```dart
final profile = fetchUserProfile();
if (profile == null) {
return;
} else {
profile = profile.name + " - profile";
}
**No value at all** (`None`): The configuration value isn't defined at all.

final preferences = fetchUserPreferences();
if (preferences == null) {
return;
}
**A known absence of a value** (`Some(None)`): The configuration value is explicitly disabled.

print('Profile: $profile, Preferences: $preferences');
```
**A present value** (`Some(Some(value))`): The configuration value is explicitly set to value.

With nullable types, a separate field or enum/sealed class would be needed to keep track of this.

- Correctness of code and reducing bugs. As to why, e.g. consider `nth` which returns the nth index
of an iterable or null if the iterable does not have an nth index.
If the iterable is `Iterable<T?>`, then a null value from calling `nth` means the nth element is
either null or the iterable does not have n elements. While if `nth` rather returned `Option`,
if the nth index is null it returns `Some(null)` and if it does not have n elements it returns `None`.
One might accidentally mishandle the nullable case and assume the `nth` index does not actually exist,
when it is rather just null. While the second case with `Option` one is force to handle both cases.
This holds true for a lot of operations that might have unintended effects
e.g. `filterMap` - since null can be a valid state that should not be filtered.

These issues are not insurmountable, and in fact, most of the time nullable types are probably more concise
and easier to deal with. Therefore, for every method in this library that uses `T?` there is also an `Option`
version, usually suffixed with `..Opt`.

#### Drawbacks
Currently in Dart, one cannot rebind variables and `Option` does not support type promotion like nullable types.
> In some languages (like Rust, not Dart) `Option` can be passed around like a reference
> and values can be taken in and out of (transmutation). Thus visible to all with reference
> to the `Option`, unlike null. Implementing such an equivalence in Dart would remove pattern
> matching and const-ness.

#### Why Not To Use Option

- Null chaining operations with `?` is not possible with `Option`

- Currently in Dart, one cannot rebind variables and `Option` does not support type promotion like nullable types.
This makes using `Option` less ergonomic in some scenarios.
```dart
Option<int> xOpt = optionFunc();
int x;
switch(xOpt) {
Some(:final v):
case Some(:final v):
x = v;
default:
case _:
return;
}
// use `int` x
Expand All @@ -137,16 +127,15 @@ if(x == null){
}
// use `int` x
```
Fortunately, since `Option` is an extension type of `T?`. This can be overcome with no runtime cost.
Fortunately, it can be converted back and forth.
```dart
int? x = optionFunc().value;
int? x = optionFunc().toNullable();
if(x == null){
return;
}
// use `int` x
```

#### Conclusion
If you can't decide between the two, it is recommended to use the `Option` type as the return type, since it allows
early return, chaining operations, and easy conversion to a nullable type with `.v`/`.value`. But the choice is up to the developer.
You can easily use this package and never use `Option`.

The choice to use `Option` is up to the developer. You can easily use this package and never use `Option`.
5 changes: 5 additions & 0 deletions book/src/libs/panic/panic.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,9 @@ throw Unreachable();
unreachable();
```

### Handling Panic

For the most part, a panic is meant to abort your program. Thus one should only try to handle panics
sparingly, likely only at the root. Use `panicHandler` or `panicHandlerAsync` if this is desired.

[How to Never Unwrap Incorrectly]:https://github.com/mcmah309/rust/tree/master/lib/src/result#how-to-never-unwrap-incorrectly
Loading
Loading