Experimental

TryExperimental.ResultType
TryExperimental.Result{T,E}

A super type of Ok{<:T}, Err{<:E}, and ConcreteResult{T,E}.

See also: Try.Ok, Try.Err, ConcreteResult.

Extended help

Examples

Consider creating an API tryparse(T, input) -> result. To simplify the example, let us define the implementation using Base.tryparse:

using Try
using TryExperimental
using TryExperimental: Result

struct InvalidCharError <: Exception end
struct EndOfBufferError <: Exception end

function __tryparse__(::Type{Int}, str::AbstractString)
    isempty(str) && return Err(EndOfBufferError())
    Ok(@something(Base.tryparse(Int, str), return Err(InvalidCharError())))
end
nothing  # hide
# output

where __tryparse__ is an overload-only API. If it is decided that the call API tryparse should have a limited set of failure modes, it can be enforced by the return value conversion to a Result type.

tryparse(T, input)::Result{T,Union{InvalidCharError,EndOfBufferError}} =
    __tryparse__(T, input)
nothing  # hide
# output
julia> tryparse(Int, "111")
Try.Ok: 111

julia> tryparse(Int, "one")
Try.Err: InvalidCharError()

Discussion

Currently, Result is defined as

Result{T,E} = Union{ConcreteResult{T,E},Ok{<:T},Err{<:E}}

although there are other possible definitions:

Result{T,E} = Union{ConcreteResult{<:T,<:E},Ok{<:T},Err{<:E}}
Result{T,E} = Union{ConcreteResult{T,E},Ok{T},Err{E}}
Result{T,E} = AbstractResult{<:T, <:E}
Result = AbstractResult

The current definition of Result may look strange since the type parameters are invariant for ConcreteResult and variant for Ok and Err. This is chosen given the expectation that Union{Ok,Err} users likely to prefer to let the compiler extra opportunities to perform pass-dependent optimizations while ConcreteResult users likely to prefer control the exact return type. The definition of Result allows these usages simultaneously.

This let __tryparse__ implementers opt-in ConcreteResult by simply converting their return value to a ConcreteResult:

function __tryparse__(T::Type, io::MyIO)::ConcreteResult{T,InvalidCharError}
    ...
end

This example also demonstrates that ConcreteResult{T,InvalidCharError} can automatically be converted to Result{T,Union{InvalidCharError,EndOfBufferError}}.

As explained above, Result{T,E} seems to have nice properties. However, it is not clear if it works in practice. This is why Result is provided from TryExperimental but not from Try. For example, ConcreteResult may not be useful in practice. If ConcreteResult is dropped, it may be a good idea to define

Result{T,E} = Union{Ok{T},Err{E}}

so that the users can explicitly manipulate the variance of each parameters.

source
TryExperimental.ConcreteResultType
TryExperimental.ConcreteResult{T,E}

Similar to Union{Ok{T},Err{E}} but it is a concrete type.

Examples

julia> using Try

julia> using TryExperimental: ConcreteResult

julia> convert(ConcreteResult{Symbol,Union{BoundsError,DomainError}}, Ok(:a))
TryExperimental.ConcreteResult (Ok): :a
source

Customizing short-circuit evaluation

TryExperimental.branchFunction
TryExperiment.branch(result) -> Continue(result)
TryExperiment.branch(result) -> Break(result)

branch implements a short-circuiting evaluation API. It must return a Continue or a Break.

source
TryExperimental.resultofFunction
TryExperimental.resultof(branch) -> result 
TryExperimental.resultof(branch::Continue{<:Ok}) ->  result::Ok
TryExperimental.resultof(branch::Break{<:Err}) ->  result::Err
TryExperimental.resultof(branch::Continue{<:Some}) ->  result::Some
TryExperimental.resultof(branch::Break{Nothing}) -> nothing
source
TryExperimental.valueofFunction
TryExperimental.valueof(branch) -> value
TryExperimental.valueof(branch::Continue{Ok{T}}) -> value::T
TryExperimental.valueof(branch::Break{Err{T}}) -> value::T
TryExperimental.valueof(branch::Continue{Some{T}}) -> value::T
TryExperimental.valueof(branch::Break{Nothing}) -> nothing
source

"Tryable" function

TryExperimental.@tryableMacro
TryExperimental.@tryable name

Create a function that can be called without causing a MethodError.

Note that Ok and Err values can be used in arbitrary functions. @tryablefn is simply a shorthand for defining a fallback implementation

fn(args...; kwargs...) = Err(NotImplementedError(fn, args, values(kwargs)))

(and auxiliary methods like istryable) to help the "Easier to ask for forgiveness than permission" (EAFP) approach.

See also: istryable.

Examples

julia> using TryExperimental: @tryable

julia> @tryable fn;

julia> fn
fn (tryable function with 1 method)

julia> fn(1)
Try.Err: Not Implemented: fn(1)
source
TryExperimental.istryableFunction
Try.istryable(callable::Any) -> bool::Bool

Check if a callable can be called without causing a MethodError.

See also: @tryable.

Examples

julia> using TryExperimental: @tryable, istryable

julia> @tryable fn;

julia> istryable(fn)
true

julia> istryable(identity)
false
source