This commit is contained in:
Francesco Mecca 2024-12-07 12:17:48 +01:00
parent 4917139553
commit c3e1d26ab3
4 changed files with 104 additions and 13 deletions

View file

@ -1,12 +1,86 @@
module Pentole.Result
namespace Pentole
type Result<'o, 'e> with
module Result =
let inline protect ([<InlineIfLambda>]f) x =
try
Ok (f x)
with e -> Error e
let inline pairwise_map fun_ (x: 'a, y: 'a) =
match fun_ x with
| Error e -> Error e
| Ok o ->
match fun_ y with | Ok o' -> Ok (o, o') | Error e -> Error e
let of_option = function | Some s -> Ok s | None -> Error ()
let zip a b =
match (a, b) with
| Ok a, Ok b -> Ok (a, b)
| Error e, _ -> Error e
| _, Error e -> Error e
module Unsafe =
/// <summary>
/// `o` if `Ok o` else throw
/// </summary>
/// <param name="result"></param>
let inline get r =
match r with
| Ok o -> o
| Error e ->
$"Tried to access value from Result r, error={e}"
|> System.ArgumentException
|> raise
/// Module to operate on lists
module ResultList =
/// <summary>
/// `o` if `Ok o` else throw
/// Transforms a sequence by applying a function that returns a Result, collecting successful results or short-circuiting on the first error.
/// </summary>
/// <param name="result"></param>
static member get (result: Result<'o, 'e>) =
match result with
| Ok o -> o
| Error e -> failwith $"Can't unwrap Error {e}"
///
/// <param name="lambda">A function that transforms each element and returns a Result</param>
/// <param name="seq_">The input sequence to transform</param>
/// <returns>
/// <list>
/// - Ok of a list containing all successfully transformed elements if all transformations succeed
/// </list>
/// <list>
/// - Error with the first encountered error if any transformation fails
/// </list>
/// </returns>
/// <example>
/// Basic usage:
///
/// <code>
/// let parseInts = ResultList.collect int "123,456,789".Split(',')
/// // Returns Ok [123; 456; 789]
/// </code>
///
///
/// Error handling:
/// <code>
/// let parseInts = ResultList.collect (Result.protect int) ["123"; "abc"; "456"]
/// // Returns Error (FormatException "The input string 'abc' was not in a correct format.")
/// </code>
///
///
/// Custom transformation:
///
/// <code>
/// let validatePositive x =
/// if x > 0 then Ok x else Error "Negative value"
/// let result = ResultList.collect validatePositive [1; 2; -3; 4]
/// // Returns Error "Negative value"
/// </code>
/// </example>
let collect (lambda: 'a -> Result<'ok, 'err>) (seq_: 'a seq) =
let rec iter_ acc seq_ =
match Seq.tryHead seq_ with
| None -> Ok acc
| Some x ->
match lambda x with
| Error e -> Error e
| Ok o -> iter_ (o::acc) (Seq.tail seq_)
iter_ [] seq_

View file

@ -2,9 +2,9 @@ module Tests.Path
open NUnit.Framework
open Pentole
open Pentole.TestsExtensions
open Pentole.Path
open Pentole.Result
[<Test>]
let constructor_test () =
@ -42,8 +42,8 @@ let resolve_test () =
s
|> Path.of_string
|> Result.map FileSystem.resolve
|> Result.get
let p (n: string ) = Path.of_string n |> Result.get
|> Result.Unsafe.get
let p (n: string ) = Path.of_string n |> Result.Unsafe.get
"/" |> test |> Assert.ok_is_equal (p "/")
"/etc/../" |> test |> Assert.ok_is_equal (p "/")
@ -51,7 +51,7 @@ let resolve_test () =
[<Test>]
let equality_test () =
let p (n: string ) = Path.of_string n |> Result.get
let p (n: string ) = Path.of_string n |> Result.Unsafe.get
Assert.are_equal (p "/etc") (p "/etc/")
(*
[<Test>]

16
tests/result_tests.fs Normal file
View file

@ -0,0 +1,16 @@
module Tests.Result
open NUnit.Framework
open Pentole.TestsExtensions
open Pentole
[<Test>]
let err_test () =
let got = ResultList.collect (Result.protect int) ["123"; "abc"; "456"]
let msg = "The input string 'abc' was not in a correct format."
match got with
| Ok _ -> Assert.Fail "Expected an error"
| Error exn -> Assert.are_equal exn.Message msg

View file

@ -10,6 +10,7 @@
<ItemGroup>
<Compile Include="bytes_tests.fs" />
<Compile Include="result_tests.fs" />
<Compile Include="string_tests.fs" />
<Compile Include="path_tests.fs" />
<Compile Include="Program.fs" />