From 90bf4521f6de168cba3bfdcc5a14af657b2091b7 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Mon, 21 Mar 2022 06:12:06 -0700 Subject: [PATCH] Add more documentation (#31) --- docs/src/experimental.md | 1 + docs/src/index.md | 25 ++++++ lib/TryExperimental/src/TryExperimental.jl | 1 + .../src/docs/ConcreteResult.md | 13 +++ lib/TryExperimental/src/docs/Result.md | 89 ++++++++++++++++++- lib/TryExperimental/src/show.jl | 19 ++++ src/core.jl | 2 +- src/docs/@function.md | 19 ++++ src/docs/Err.md | 15 ++++ src/docs/Ok.md | 15 ++++ src/docs/disable_errortrace.md | 5 ++ src/docs/enable_errortrace.md | 31 +++++++ src/docs/errtype.md | 15 ++++ src/docs/iserr.md | 15 ++++ src/docs/isok.md | 15 ++++ src/docs/istryable.md | 20 +++++ src/docs/oktype.md | 15 ++++ src/docs/unwrap.md | 15 ++++ src/docs/unwrap_err.md | 12 +++ 19 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 lib/TryExperimental/src/docs/ConcreteResult.md create mode 100644 lib/TryExperimental/src/show.jl create mode 100644 src/docs/@function.md create mode 100644 src/docs/disable_errortrace.md create mode 100644 src/docs/enable_errortrace.md create mode 100644 src/docs/errtype.md create mode 100644 src/docs/iserr.md create mode 100644 src/docs/isok.md create mode 100644 src/docs/istryable.md create mode 100644 src/docs/oktype.md create mode 100644 src/docs/unwrap.md create mode 100644 src/docs/unwrap_err.md diff --git a/docs/src/experimental.md b/docs/src/experimental.md index 3151f2d..1c1b3d1 100644 --- a/docs/src/experimental.md +++ b/docs/src/experimental.md @@ -2,6 +2,7 @@ ```@docs TryExperimental.Result +TryExperimental.ConcreteResult ``` ## [Customizing short-circuit evaluation](@id customize-short-circuit) diff --git a/docs/src/index.md b/docs/src/index.md index 283dd1e..3a87264 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -2,8 +2,19 @@ ```@docs Try +``` + +## Result value manipulation API + +```@docs Ok Err +Try.isok +Try.iserr +Try.unwrap +Try.unwrap_err +Try.oktype +Try.errtype ``` ## Short-circuit evaluation @@ -16,3 +27,17 @@ Try.and_then ``` See also: [Customizing short-circuit evaluation](@ref customize-short-circuit). + +## "Tryable" function + +```@docs +Try.@function +Try.istryable +``` + +## Debugging interface (error traces) + +```@docs +Try.enable_errortrace +Try.disable_errortrace +``` diff --git a/lib/TryExperimental/src/TryExperimental.jl b/lib/TryExperimental/src/TryExperimental.jl index 3fff29b..37ee653 100644 --- a/lib/TryExperimental/src/TryExperimental.jl +++ b/lib/TryExperimental/src/TryExperimental.jl @@ -67,6 +67,7 @@ include("concrete.jl") include("sugars.jl") include("causes.jl") include("base.jl") +include("show.jl") end # module Internal diff --git a/lib/TryExperimental/src/docs/ConcreteResult.md b/lib/TryExperimental/src/docs/ConcreteResult.md new file mode 100644 index 0000000..35a90bc --- /dev/null +++ b/lib/TryExperimental/src/docs/ConcreteResult.md @@ -0,0 +1,13 @@ + TryExperimental.ConcreteResult{T,E} + +Similar to `Union{Ok{T},Err{E}}` but it is a concrete type. + +# Examples +```julia +julia> using Try + +julia> using TryExperimental: ConcreteResult + +julia> convert(ConcreteResult{Symbol,Union{BoundsError,DomainError}}, Ok(:a)) +TryExperimental.ConcreteResult (Ok): :a +``` diff --git a/lib/TryExperimental/src/docs/Result.md b/lib/TryExperimental/src/docs/Result.md index fc222ea..98a1dde 100644 --- a/lib/TryExperimental/src/docs/Result.md +++ b/lib/TryExperimental/src/docs/Result.md @@ -1 +1,88 @@ - Result{T,E} + TryExperimental.Result{T,E} + +A super type of `Ok{<:T}`, `Err{<:E}`, and `ConcreteResult{T,E}`. + +See also: [`Try.Ok`](@ref), [`Try.Err`](@ref), [`ConcreteResult`](@ref). + +# Extended help +# Examples + +Consider creating an API `tryparse(T, input) -> result`. To simplify the example, let us +define the implementation using `Base.tryparse`: + +```julia +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 +``` + +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. + +```julia +tryparse(T, input)::Result{T,Union{InvalidCharError,EndOfBufferError}} = + __tryparse__(T, input) +``` + +```julia +julia> tryparse(Int, "111") +Try.Ok: 111 + +julia> tryparse(Int, "one") +Try.Err: InvalidCharError() +``` + +## Discussion + +Currently, `Result` is defined as + +```JULIA +Result{T,E} = Union{ConcreteResult{T,E},Ok{<:T},Err{<:E}} +``` + +although there are other possible definitions: + +```JULIA +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`: + +```JULIA +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. diff --git a/lib/TryExperimental/src/show.jl b/lib/TryExperimental/src/show.jl new file mode 100644 index 0000000..0fd24d3 --- /dev/null +++ b/lib/TryExperimental/src/show.jl @@ -0,0 +1,19 @@ +function Base.show(io::IO, ::MIME"text/plain", result::ConcreteResult) + print(io, "TryExperimental.ConcreteResult ") + value = result.value + if value isa Ok + printstyled(io, "(Ok)"; color = :green, bold = true) + print(io, ": ") + show(io, MIME"text/plain"(), Try.unwrap(value)) + else + printstyled(io, "(Err)"; color = :red, bold = true) + print(io, ": ") + ex = Try.unwrap_err(err) + backtrace = err.backtrace + if backtrace === nothing + showerror(io, ex) + else + showerror(io, ex, simplify_backtrace(err.backtrace)) + end + end +end diff --git a/src/core.jl b/src/core.jl index 1a686a4..11c3958 100644 --- a/src/core.jl +++ b/src/core.jl @@ -1,7 +1,7 @@ Try.Ok(::Type{T}) where {T} = Try.Ok{Type{T}}(T) Try.Err(value) = Try.Err(value, maybe_backtrace()) -Try.Err{E}(value) where {E<:Exception} = Try.Err{E}(value, maybe_backtrace()) +Try.Err{E}(value) where {E} = Try.Err{E}(value, maybe_backtrace()) Try.unwrap(ok::Ok) = ok.value Try.unwrap(err::Err) = _throw(err) diff --git a/src/docs/@function.md b/src/docs/@function.md new file mode 100644 index 0000000..0011452 --- /dev/null +++ b/src/docs/@function.md @@ -0,0 +1,19 @@ + Try.@function name + +Create a function that can be called without causing a `MethodError`. + +See also: [`istryable`](@ref). + +# Examples + +```julia +julia> using Try + +julia> Try.@function fn; + +julia> fn +fn (tryable function with 1 method) + +julia> fn(1) +Try.Err: Not Implemented: fn(1) +``` diff --git a/src/docs/Err.md b/src/docs/Err.md index e7e83ca..ce44bb2 100644 --- a/src/docs/Err.md +++ b/src/docs/Err.md @@ -1,2 +1,17 @@ Err(value::E) -> err::Err{E} Err{E}(value) -> err::Err{E} + +Indicate that `value` is a "failure" in a sense defined by the API returning this value. + +See: [`iserr`](@ref), [`unwrap_err`](@ref) + +# Examples +```julia +julia> using Try + +julia> result = Err(1) +Try.Err: 1 + +julia> Try.unwrap_err(result) +1 +``` diff --git a/src/docs/Ok.md b/src/docs/Ok.md index 8fd8cd6..e1cb1c6 100644 --- a/src/docs/Ok.md +++ b/src/docs/Ok.md @@ -1,2 +1,17 @@ Ok(value::T) -> ok::Ok{T} Ok{T}(value) -> ok::Ok{T} + +Indicate that `value` is a "success" in a sense defined by the API returning this value. + +See also: [`isok`](@ref), [`unwrap`](@ref) + +# Examples +```julia +julia> using Try + +julia> result = Ok(1) +Try.Ok: 1 + +julia> Try.unwrap(result) +1 +``` diff --git a/src/docs/disable_errortrace.md b/src/docs/disable_errortrace.md new file mode 100644 index 0000000..735a0c0 --- /dev/null +++ b/src/docs/disable_errortrace.md @@ -0,0 +1,5 @@ + Try.disable_errortrace() + +Disable stack trace capturing. + +See also: [`Try.enable_errortrace`](@ref). diff --git a/src/docs/enable_errortrace.md b/src/docs/enable_errortrace.md new file mode 100644 index 0000000..f2ccf19 --- /dev/null +++ b/src/docs/enable_errortrace.md @@ -0,0 +1,31 @@ + Try.enable_errortrace() + +Enable stack trace capturing for each `Err` value creation for debugging. + +See also: [`Try.disable_errortrace`](@ref) + +# Examples +```JULIA +julia> using Try, TryExperimental + +julia> trypush!(Int[], :a) +Try.Err: Not Implemented: tryconvert(Int64, :a) + +julia> Try.enable_errortrace(); + +julia> trypush!(Int[], :a) +Try.Err: Not Implemented: tryconvert(Int64, :a) +Stacktrace: + [1] convert + @ ~/.julia/dev/Try/src/core.jl:28 [inlined] + [2] Break (repeats 2 times) + @ ~/.julia/dev/Try/src/branch.jl:11 [inlined] + [3] branch + @ ~/.julia/dev/Try/src/branch.jl:27 [inlined] + [4] macro expansion + @ ~/.julia/dev/Try/src/branch.jl:49 [inlined] + [5] (::TryExperimental.var"##typeof_trypush!#298")(a::Vector{Int64}, x::Symbol) + @ TryExperimental.Internal ~/.julia/dev/Try/lib/TryExperimental/src/base.jl:69 + [6] top-level scope + @ REPL[4]:1 +``` diff --git a/src/docs/errtype.md b/src/docs/errtype.md new file mode 100644 index 0000000..c00cb59 --- /dev/null +++ b/src/docs/errtype.md @@ -0,0 +1,15 @@ + Try.errtype(::Type{Err{E}}) -> E::Type + Try.errtype(::Err{E}) -> E::Type + +Get the type of the value stored in an `Err`. + +# Examples +```julia +julia> using Try + +julia> Try.errtype(Err{Symbol}) +Symbol + +julia> Try.errtype(Err(:a)) +Symbol +``` diff --git a/src/docs/iserr.md b/src/docs/iserr.md new file mode 100644 index 0000000..e606c7f --- /dev/null +++ b/src/docs/iserr.md @@ -0,0 +1,15 @@ + Try.iserr(::Err) -> true + Try.iserr(::Ok) -> false + +Return `true` on an [`Err`](@ref); return `false` on an [`Ok`](@ref). + +# Examples +```julia +julia> using Try + +julia> Try.iserr(Try.Err(1)) +true + +julia> Try.iserr(Try.Ok(1)) +false +``` diff --git a/src/docs/isok.md b/src/docs/isok.md new file mode 100644 index 0000000..602cfb1 --- /dev/null +++ b/src/docs/isok.md @@ -0,0 +1,15 @@ + Try.isok(::Ok) -> true + Try.isok(::Err) -> false + +Return `true` on an [`Ok`](@ref); return `false` on an [`Err`](@ref). + +# Examples +```julia +julia> using Try + +julia> Try.isok(Try.Ok(1)) +true + +julia> Try.isok(Try.Err(1)) +false +``` diff --git a/src/docs/istryable.md b/src/docs/istryable.md new file mode 100644 index 0000000..cf83c0e --- /dev/null +++ b/src/docs/istryable.md @@ -0,0 +1,20 @@ + Try.istryable(callable::Any) -> bool::Bool + +Check if a `callable` can be called without causing a `MethodError`. + +See also: [`Try.@function`](@ref). + +# Examples + +```julia +julia> using Try + +julia> Try.@function fn; + +julia> Try.istryable(fn) +true + +julia> Try.istryable(identity) +false +``` + diff --git a/src/docs/oktype.md b/src/docs/oktype.md new file mode 100644 index 0000000..15fbc06 --- /dev/null +++ b/src/docs/oktype.md @@ -0,0 +1,15 @@ + Try.oktype(::Type{Ok{T}}) -> T::Type + Try.oktype(::Ok{T}) -> T::Type + +Get the type of the value stored in an `Ok`. + +# Examples +```julia +julia> using Try + +julia> Try.oktype(Ok{Symbol}) +Symbol + +julia> Try.oktype(Ok(:a)) +Symbol +``` diff --git a/src/docs/unwrap.md b/src/docs/unwrap.md new file mode 100644 index 0000000..0e39e5f --- /dev/null +++ b/src/docs/unwrap.md @@ -0,0 +1,15 @@ + Try.unwrap(Ok(value)) -> value + Try.unwrap(::Err) # throws + +Unwrap an [`Ok`](@ref) value; throws on an [`Err`](@ref). + +To obtain a stack trace to the place `Err` is constructed (and not where `unwrap` is +called), use [`Try.enable_errortrace`](@ref). + +# Examples +```julia +julia> using Try + +julia> Try.unwrap(Try.Ok(1)) +1 +``` diff --git a/src/docs/unwrap_err.md b/src/docs/unwrap_err.md new file mode 100644 index 0000000..426f3a9 --- /dev/null +++ b/src/docs/unwrap_err.md @@ -0,0 +1,12 @@ + Try.unwrap_err(Err(value)) -> value + Try.unwrap_err(::Ok) # throws + +Unwrap an [`Err`](@ref) value; throws on an [`Ok`](@ref). + +# Examples +```julia +julia> using Try + +julia> Try.unwrap_err(Try.Err(1)) +1 +```