All Projects → iamed2 → Resulttypes.jl

iamed2 / Resulttypes.jl

Licence: other
A Result type for Julia—it's like Nullables for Exceptions

Programming Languages

julia
2034 projects

Projects that are alternatives of or similar to Resulttypes.jl

Httperrorpages
⏩ Simple HTTP Error Page Generator
Stars: ✭ 772 (+1186.67%)
Mutual labels:  error-handling
Make Error Cause
Make your own nested errors
Stars: ✭ 36 (-40%)
Mutual labels:  error-handling
Cvdm.errorhandling
[DEPRECATED, use FsToolkit.ErrorHandling] AsyncResult and Result computation expressions and helper functions for error handling in F#
Stars: ✭ 47 (-21.67%)
Mutual labels:  error-handling
Thoth
An Error Logger for Go
Stars: ✭ 22 (-63.33%)
Mutual labels:  error-handling
Rbdl Orb
RBDL - Rigid Body Dynamics Library - ORB Version - The two main differences to the original rbdl is that this version has error handling and uses polymorphism for constraints
Stars: ✭ 33 (-45%)
Mutual labels:  error-handling
Friendly Exception
An interface for an exception to be friendly
Stars: ✭ 41 (-31.67%)
Mutual labels:  error-handling
Bugsnag Laravel
Bugsnag notifier for the Laravel PHP framework. Monitor and report Laravel errors.
Stars: ✭ 746 (+1143.33%)
Mutual labels:  error-handling
Searcher
Query Search Portals from R
Stars: ✭ 59 (-1.67%)
Mutual labels:  error-handling
Perhaps
A monad, perhaps.
Stars: ✭ 35 (-41.67%)
Mutual labels:  error-handling
Progress Activity
Easily add loading, empty and error states in your app.
Stars: ✭ 1,039 (+1631.67%)
Mutual labels:  error-handling
Await Handler
Basic wrapper for await that allows handling of errors without try/catch blocks
Stars: ✭ 13 (-78.33%)
Mutual labels:  error-handling
Whoops
PSR-15 middleware to use Whoops as error handler
Stars: ✭ 29 (-51.67%)
Mutual labels:  error-handling
Rollbar Android
Rollbar for Android
Stars: ✭ 41 (-31.67%)
Mutual labels:  error-handling
Error Overlay Webpack Plugin
Catch errors with style 💥✨
Stars: ✭ 821 (+1268.33%)
Mutual labels:  error-handling
Bugsnag Node
[DEPRECATED] Please upgrade to our Universal JS notifier "@bugsnag/js" • https://github.com/bugsnag/bugsnag-js
Stars: ✭ 48 (-20%)
Mutual labels:  error-handling
Eris
eris provides a better way to handle, trace, and log errors in Go 🎆
Stars: ✭ 758 (+1163.33%)
Mutual labels:  error-handling
Happy
the alchemist's happy path with elixir
Stars: ✭ 37 (-38.33%)
Mutual labels:  error-handling
Go Errortree
Go library for handling errors organized as a tree
Stars: ✭ 59 (-1.67%)
Mutual labels:  error-handling
Panic Overlay
Displays JS errors in browsers. Shows sources. Use with any framework. 💥✨
Stars: ✭ 50 (-16.67%)
Mutual labels:  error-handling
Bugsnag Android Ndk
DEPRECATED - this project now lives at bugsnag/bugsnag-android
Stars: ✭ 42 (-30%)
Mutual labels:  error-handling

ResultTypes

Stable Dev Build Status Codecov

ResultTypes provides a Result type which can hold either a value or an error. This allows us to return a value or an error in a type-stable manner without throwing an exception.

Usage

Basic

We can construct a Result that holds a value:

julia> x = Result(2); typeof(x)
ResultTypes.Result{Int64,ErrorException}

or a Result that holds an error:

julia> x = ErrorResult(Int, "Oh noes!"); typeof(x)
ResultTypes.Result{Int64,ErrorException}

or either with a different error type:

julia> x = Result(2, DivideError); typeof(x)
ResultTypes.Result{Int64,DivideError}

julia> x = ErrorResult(Int, DivideError()); typeof(x)
ResultTypes.Result{Int64,DivideError}

Exploiting Function Return Types

We can take advantage of automatic conversions in function returns (a Julia 0.5 feature):

function integer_division(x::Int, y::Int)::Result{Int, DivideError}
    if y == 0
        return DivideError()
    else
        return div(x, y)
    end
end

This allows us to write code in the body of the function that returns either a value or an error without manually constructing Result types.

julia> integer_division(3, 4)
Result(0)

julia> integer_division(3, 0)
ErrorResult(Int64, DivideError())

Evidence of Benefits

Theoretical

Using the function above, we can use @code_warntype to verify that the compiler is doing what we desire:

julia> @code_warntype integer_division(3, 2)
Body::Result{Int64,DivideError}
2 1  %1 = (y === 0)::Bool                                                                                       │╻     ==
  └──      goto #3 if not %1                                                                                     │
3 2  %3 = %new(Result{Int64,DivideError}, nothing, $(QuoteNode(DivideError())))::Result{Int64,DivideError}      │╻╷    convert
  └──      return %3                                                                                             
5 3  %5 = (Base.checked_sdiv_int)(x, y)::Int64                                                                  │╻     div
     %6 = %new(Some{Int64}, %5)::Some{Int64}                                                                    ││╻╷╷╷  Type
     %7 = %new(Result{Int64,DivideError}, %6, nothing)::Result{Int64,DivideError}                               │││
  └──      return %7                                                                                             

Experimental

Suppose we have two versions of a function where one returns a value or throws an exception and the other returns a Result type. We want to call the function and return the value if present or a default value if there was an error. For this example we can use div and our integer_division function as a microbenchmark (they are too simple to provide a realistic use case). We'll use @noinline to ensure the functions don't get inlined, which will make the benchmarks more comparable.

Here's our wrapping function for div:

@noinline function func1(x, y)
    local z
    try
        z = div(x, y)
    catch e
        z = 0
    end
    return z
end

and for integer_division:

@noinline function func2(x, y)
    r = integer_division(x, y)
    if ResultTypes.iserror(r)
        return 0
    else
        return unwrap(r)
    end
end

Here are some benchmark results in the average case (on one machine), using BenchmarkTools.jl:

julia> using BenchmarkTools, Statistics

julia> t1 = @benchmark for i = 1:10 func1(3, i % 2) end
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     121.664 μs (0.00% GC)
  median time:      122.652 μs (0.00% GC)
  mean time:        124.350 μs (0.00% GC)
  maximum time:     388.198 μs (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> t2 = @benchmark for i = 1:10 func2(3, i % 2) end
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     18.853 ns (0.00% GC)
  median time:      21.078 ns (0.00% GC)
  mean time:        21.183 ns (0.00% GC)
  maximum time:     275.057 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     997

julia> judge(mean(t2), mean(t1))
BenchmarkTools.TrialJudgement:
  time:   -99.98% => improvement (5.00% tolerance)
  memory: +0.00% => invariant (1.00% tolerance)

As we can see, we get a huge speed improvement without allocating any extra heap memory.

It's also interesting to look at the cost when no error occurs:

julia> t1 = @benchmark for i = 1:10 func1(3, 1) end
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     115.060 ns (0.00% GC)
  median time:      118.042 ns (0.00% GC)
  mean time:        118.616 ns (0.00% GC)
  maximum time:     279.901 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     918

julia> t2 = @benchmark for i = 1:10 func2(3, 1) end
BenchmarkTools.Trial:
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     28.775 ns (0.00% GC)
  median time:      30.516 ns (0.00% GC)
  mean time:        31.290 ns (0.00% GC)
  maximum time:     74.936 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     995

julia> judge(mean(t2), mean(t1))
BenchmarkTools.TrialJudgement:
  time:   -73.62% => improvement (5.00% tolerance)
  memory: +0.00% => invariant (1.00% tolerance)

It's still faster to avoid try and use Result, even when the error condition is never triggered.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].