Skip to content

Commit

Permalink
Add "and" and "or" functions and macros (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
tkf authored Apr 24, 2022
1 parent cd718fa commit 3a09700
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 1 deletion.
4 changes: 4 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Try.@and_return
Try.@return
Try.or_else
Try.and_then
Try.@or
Try.@and
Try.or
Try.and
```

See also: [Customizing short-circuit evaluation](@ref customize-short-circuit).
Expand Down
6 changes: 5 additions & 1 deletion src/Try.jl
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,18 @@ end
macro and_return end
function var"@?" end
function var"@return" end
macro and end
macro or end

function and end
function and_then end
function or end
function or_else end
function unwrap_or_else end

module Internal

import ..Try: @and_return, @?
import ..Try: @and_return, @?, @and, @or
using ..Try: Err, Ok, Try
using ..Try.InternalPrelude: AbstractResult, _IsOkError

Expand Down
56 changes: 56 additions & 0 deletions src/branch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,62 @@ function Try.unwrap_or_else(f, result)
end
end

Try.and(result) = result

function Try.and(a, b)
br = branch(a)
if br isa Break
br.result
else
b
end
end

Try.and(a, b, c, rest...) = Try.and(Try.and(a, b), c, rest...)

Try.or(result) = result

function Try.or(a, b)
br = branch(a)
if br isa Continue
br.result
else
b
end
end

Try.or(a, b, c, rest...) = Try.or(Try.or(a, b), c, rest...)

macro and(ex, rest...)
exprs = map(esc, Any[ex, rest...])
foldr(exprs; init = pop!(exprs)) do result, ex
br = esc(gensym(:br))
quote
$br = branch($result)
if $br isa Break
$br.result
else
$ex
end
end
end
end

macro or(ex, rest...)
exprs = map(esc, Any[ex, rest...])
foldr(exprs; init = pop!(exprs)) do result, ex
br = esc(gensym(:br))
quote
$br = branch($result)
if $br isa Continue
$br.result
else
$ex
end
end
end
end

###
### Currying
###
Expand Down
24 changes: 24 additions & 0 deletions src/docs/@and.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Try.@and(expressions...)

Evaluate to the first "failure" result of one of the `expressions` and do not evaluate
the rest of the `expressions`. Otherwise, evaluate all `expressions` and return the last
result.

# Extended help

## Examples

```julia
julia> using Try

julia> Try.@and(Ok(1), Ok(2), Ok(3))
Try.Ok: 3

julia> Try.@and(Ok(1), Err(2), (println("this is not evaluated"); Ok(3)))
Try.Err: 2

julia> Try.@and(Some(1), Some(2), Some(3))
Some(3)

julia> Try.@and(Some(1), nothing, (println("this is not evaluated"); Some(3)))
```
24 changes: 24 additions & 0 deletions src/docs/@or.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Try.@or(expressions...)

Evaluate to the first "successful" result of one of the `expressions` and do not evaluate
the rest of the `expressions`. Otherwise, evaluate all `expressions` and return the last
result.

# Extended help

## Examples

```julia
julia> using Try

julia> Try.@or(Err(1), Ok(2), (println("this is not evaluated"); Err(3)))
Try.Ok: 2

julia> Try.@or(Err(1), Err(2), Err(3))
Try.Err: 3

julia> Try.@or(nothing, Some(2), (println("this is not evaluated"); Some(3)))
Some(2)

julia> Try.@or(nothing, nothing, nothing)
```
22 changes: 22 additions & 0 deletions src/docs/and.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Try.and(results...)

Return the first "failure" or the last result if all results are "success."

# Extended help

## Examples

```julia
julia> using Try

julia> Try.and(Ok(1), Ok(2), Ok(3))
Try.Ok: 3

julia> Try.and(Ok(1), Err(2), Ok(3))
Try.Err: 2

julia> Try.and(Some(1), Some(2), Some(3))
Some(3)

julia> Try.and(Some(1), nothing, Some(3))
```
22 changes: 22 additions & 0 deletions src/docs/or.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Try.or(results...)

Return the first "successful" result or the last result if all results are "failures."

# Extended help

## Examples

```julia
julia> using Try

julia> Try.or(Err(1), Ok(2), Err(3))
Try.Ok: 2

julia> Try.or(Err(1), Err(2), Err(3))
Try.Err: 3

julia> Try.or(nothing, Some(2), Some(3))
Some(2)

julia> Try.or(nothing, nothing, nothing)
```
22 changes: 22 additions & 0 deletions test/TryTests/src/test_tools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ function test_or_return()
@test Try.unwrap(try_map_prealloc2(x -> x + 1, 1:3)) == 2:4
end

function test_and()
@test Try.and(Ok(1), Ok(2), Ok(3)) == Try.Ok(3)
@test Try.and(Ok(1), Err(2), Ok(3)) == Try.Err(2)
@test Try.and(Some(1), Some(2), Some(3)) == Some(3)
@test Try.and(Some(1), nothing, Some(3)) === nothing
@test Try.@and(Ok(1), Ok(2), Ok(3)) == Try.Ok(3)
@test Try.@and(Ok(1), Err(2), Ok(3)) == Try.Err(2)
@test Try.@and(Some(1), Some(2), Some(3)) == Some(3)
@test Try.@and(Some(1), nothing, Some(3)) === nothing
end

function test_or()
@test Try.or(Err(1), Ok(2), Err(3)) == Try.Ok(2)
@test Try.or(Err(1), Err(2), Err(3)) == Try.Err(3)
@test Try.or(nothing, Some(2), Some(3)) == Some(2)
@test Try.or(nothing, nothing, nothing) === nothing
@test Try.@or(Err(1), Ok(2), Err(3)) == Try.Ok(2)
@test Try.@or(Err(1), Err(2), Err(3)) == Try.Err(3)
@test Try.@or(nothing, Some(2), Some(3)) == Some(2)
@test Try.@or(nothing, nothing, nothing) === nothing
end

function test_unwrap_or_else()
@test Try.unwrap_or_else(length, Try.Ok(1)) == 1
@test Try.unwrap_or_else(length, Try.Err("four")) == 4
Expand Down

0 comments on commit 3a09700

Please sign in to comment.