Skip to content

Commit

Permalink
Add more documentation (#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf authored Mar 21, 2022
1 parent e6ee547 commit 90bf452
Show file tree
Hide file tree
Showing 19 changed files with 340 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/src/experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

```@docs
TryExperimental.Result
TryExperimental.ConcreteResult
```

## [Customizing short-circuit evaluation](@id customize-short-circuit)
Expand Down
25 changes: 25 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
```
1 change: 1 addition & 0 deletions lib/TryExperimental/src/TryExperimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ include("concrete.jl")
include("sugars.jl")
include("causes.jl")
include("base.jl")
include("show.jl")

end # module Internal

Expand Down
13 changes: 13 additions & 0 deletions lib/TryExperimental/src/docs/ConcreteResult.md
Original file line number Diff line number Diff line change
@@ -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
```
89 changes: 88 additions & 1 deletion lib/TryExperimental/src/docs/Result.md
Original file line number Diff line number Diff line change
@@ -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.
19 changes: 19 additions & 0 deletions lib/TryExperimental/src/show.jl
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/core.jl
Original file line number Diff line number Diff line change
@@ -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)
Expand Down
19 changes: 19 additions & 0 deletions src/docs/@function.md
Original file line number Diff line number Diff line change
@@ -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)
```
15 changes: 15 additions & 0 deletions src/docs/Err.md
Original file line number Diff line number Diff line change
@@ -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
```
15 changes: 15 additions & 0 deletions src/docs/Ok.md
Original file line number Diff line number Diff line change
@@ -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
```
5 changes: 5 additions & 0 deletions src/docs/disable_errortrace.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Try.disable_errortrace()

Disable stack trace capturing.

See also: [`Try.enable_errortrace`](@ref).
31 changes: 31 additions & 0 deletions src/docs/enable_errortrace.md
Original file line number Diff line number Diff line change
@@ -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
```
15 changes: 15 additions & 0 deletions src/docs/errtype.md
Original file line number Diff line number Diff line change
@@ -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
```
15 changes: 15 additions & 0 deletions src/docs/iserr.md
Original file line number Diff line number Diff line change
@@ -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
```
15 changes: 15 additions & 0 deletions src/docs/isok.md
Original file line number Diff line number Diff line change
@@ -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
```
20 changes: 20 additions & 0 deletions src/docs/istryable.md
Original file line number Diff line number Diff line change
@@ -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
```

15 changes: 15 additions & 0 deletions src/docs/oktype.md
Original file line number Diff line number Diff line change
@@ -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
```
15 changes: 15 additions & 0 deletions src/docs/unwrap.md
Original file line number Diff line number Diff line change
@@ -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
```
12 changes: 12 additions & 0 deletions src/docs/unwrap_err.md
Original file line number Diff line number Diff line change
@@ -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
```

0 comments on commit 90bf452

Please sign in to comment.