Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more documentation #31

Merged
merged 2 commits into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
```