Experimental
TryExperimental.Result
— TypeTryExperimental.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.
TryExperimental.ConcreteResult
— TypeTryExperimental.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
Customizing short-circuit evaluation
TryExperimental.branch
— FunctionTryExperiment.branch(result) -> Continue(result)
TryExperiment.branch(result) -> Break(result)
branch
implements a short-circuiting evaluation API. It must return a Continue
or a Break
.
TryExperimental.Break
— TypeTryExperimental.Break(result)
TryExperimental.Continue
— TypeTryExperimental.Continue(result)
TryExperimental.resultof
— FunctionTryExperimental.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
TryExperimental.valueof
— FunctionTryExperimental.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
"Tryable" function
TryExperimental.@tryable
— MacroTryExperimental.@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)
TryExperimental.istryable
— FunctionTry.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