Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/kfly8/result-simple
A dead simple perl-ish Result like F#, Rust, Go, etc.
https://github.com/kfly8/result-simple
perl result-type types
Last synced: 9 days ago
JSON representation
A dead simple perl-ish Result like F#, Rust, Go, etc.
- Host: GitHub
- URL: https://github.com/kfly8/result-simple
- Owner: kfly8
- License: other
- Created: 2024-11-17T04:14:17.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2024-11-27T01:18:53.000Z (2 months ago)
- Last Synced: 2024-11-27T02:21:32.668Z (2 months ago)
- Topics: perl, result-type, types
- Language: Perl
- Homepage:
- Size: 85 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: Changes
- License: LICENSE
Awesome Lists containing this project
README
[![Actions Status](https://github.com/kfly8/Result-Simple/actions/workflows/test.yml/badge.svg)](https://github.com/kfly8/Result-Simple/actions) [![Coverage Status](https://img.shields.io/coveralls/kfly8/Result-Simple/main.svg?style=flat)](https://coveralls.io/r/kfly8/Result-Simple?branch=main) [![MetaCPAN Release](https://badge.fury.io/pl/Result-Simple.svg)](https://metacpan.org/release/Result-Simple)
# NAMEResult::Simple - A dead simple perl-ish Result like F#, Rust, Go, etc.
# SYNOPSIS
```perl
# Enable type check. The default is false.
BEGIN { $ENV{RESULT_SIMPLE_CHECK_ENABLED} = 1 }use Test2::V0;
use Result::Simple;
use Types::Common -types;use kura ErrorMessage => StrLength[3,];
use kura ValidName => sub { my (undef, $e) = validate_name($_); !$e };
use kura ValidAge => sub { my (undef, $e) = validate_age($_); !$e };
use kura ValidUser => Dict[name => ValidName, age => ValidAge];sub validate_name {
my $name = shift;
return Err('No name') unless defined $name;
return Err('Empty name') unless length $name;
return Err('Reserved name') if $name eq 'root';
return Ok($name);
}sub validate_age {
my $age = shift;
return Err('No age') unless defined $age;
return Err('Invalid age') unless $age =~ /\A\d+\z/;
return Err('Too young age') if $age < 18;
return Ok($age);
}sub new_user :Result(ValidUser, ArrayRef[ErrorMessage]) {
my $args = shift;
my @errors;my ($name, $name_err) = validate_name($args->{name});
push @errors, $name_err if $name_err;my ($age, $age_err) = validate_age($args->{age});
push @errors, $age_err if $age_err;return Err(\@errors) if @errors;
return Ok({ name => $name, age => $age });
}my ($user1, $err1) = new_user({ name => 'taro', age => 42 });
is $user1, { name => 'taro', age => 42 };
is $err1, undef;my ($user2, $err2) = new_user({ name => 'root', age => 1 });
is $user2, undef;
is $err2, ['Reserved name', 'Too young age'];
```# DESCRIPTION
Result::Simple is a dead simple Perl-ish Result.
Result represents a function's return value as success or failure, enabling safer error handling and more effective control flow management.
This pattern is used in other languages such as F#, Rust, and Go.In Perl, this pattern is also useful, and this module provides a simple way to use it.
This module does not wrap a return value in an object. Just return a tuple like `($data, undef)` or `(undef, $err)`.## EXPORT FUNCTIONS
### Ok
Return a tuple of a given value and undef. When the function succeeds, it should return this.
```perl
sub add($a, $b) {
Ok($a + $b); # => ($a + $b, undef)
}
```### Err
Return a tuple of undef and a given error. When the function fails, it should return this.
```perl
sub div($a, $b) {
return Err('Division by zero') if $b == 0; # => (undef, 'Division by zero')
Ok($a / $b);
}
```Note that the error value must be a truthy value, otherwise it will throw an exception.
## ATTRIBUTES
### :Result(T, E)
You can use the `:Result(T, E)` attribute to define a function that returns a success or failure and asserts the return value types. Here is an example:
```perl
sub half :Result(Int, ErrorMessage) ($n) {
if ($n % 2) {
return Err('Odd number');
} else {
return Ok($n / 2);
}
}
```- T (success type)
When the function succeeds, then returns `($data, undef)`, and `$data` should satisfy this type.
- E (error type)
When the function fails, then returns `(undef, $err)`, and `$err` should satisfy this type.
Additionally, type E must be truthy value to distinguish between success and failure.```perl
sub foo :Result(Int, Str) ($input) { }
# => throw exception: Result E should not allow falsy values: ["0"] because Str allows "0"
```When a function never returns an error, you can set type E to `undef`:
```perl
sub double :Result(Int, undef) ($n) { Ok($n * 2) }
```Note that types require `check` method that returns true or false. So you can use your favorite type constraint module like
[Type::Tiny](https://metacpan.org/pod/Type%3A%3ATiny), [Moose](https://metacpan.org/pod/Moose), [Mouse](https://metacpan.org/pod/Mouse) or [Data::Checks](https://metacpan.org/pod/Data%3A%3AChecks) etc.## ENVIRONMENTS
### `$ENV{RESULT_SIMPLE_CHECK_ENABLED}`
If the `ENV{RESULT_SIMPLE_CHECK_ENABLED}` environment is truthy before loading this module, it works as an assertion.
Otherwise, if it is falsy, `:Result(T, E)` attribute does nothing. The default is false.```perl
sub invalid :Result(Int, undef) { Ok("hello") }my ($data, $err) = invalid();
# => throw exception when check enabled
# => no exception when check disabled
```The following code is an example to enable it:
```perl
BEGIN { $ENV{RESULT_SIMPLE_CHECK_ENABLED} = is_test ? 1 : 0 }
use Result::Simple;
```This option is useful for development and testing mode, and it recommended to set it to false for production.
# NOTE
## Avoiding Ambiguity in Result Handling
Forgetting to call `Ok` or `Err` function is a common mistake. Consider the following example:
```perl
sub validate_name :Result(Str, ErrorMessage) ($name) {
return "Empty name" unless $name; # Oops! Forgot to call `Err` function.
return Ok($name);
}my ($name, $err) = validate_name('');
# => throw exception: Invalid result tuple (T, E)
```In this case, the function throws an exception because the return value is not a valid result tuple `($data, undef)` or `(undef, $err)`.
This is fortunate, as the mistake is detected immediately. The following case is not detected:```perl
sub foo :Result(Str, ErrorMessage) {
return (undef, 'apple'); # No use of `Ok` or `Err` function.
}my ($data, $err) = foo;
# => $err is 'apple'
```Here, the function returns a valid failure tuple `(undef, $err)`. However, it is unclear whether this was intentional or a mistake.
The lack of `Ok` or `Err` makes the intent ambiguous.Conclusively, be sure to use `Ok` or `Err` functions to make it clear whether the success or failure is intentional.
# LICENSE
Copyright (C) kobaken.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.# AUTHOR
kobaken