Maybe.jl

Note

Tutorial is the best place to start.

Maybe

MaybeModule

Maybe.jl: Optional value handling for Julia

Dev CI

Maybe.jl provides functions and macros for handling the values of type Union{Some,Nothing}; i.e., option type. The main entry point for the optional value handling is the macro @?:

julia> using Maybe

julia> data = [1, 3, 2, 1];

julia> @? data[findfirst(iseven, data)]
Some(2)

julia> y = @? data[findfirst(>=(4), data)]

julia> @assert y === nothing

Maybe.jl also provides low-level functions such as Maybe.get which is the "Maybe" variant of Base.get:

julia> Maybe.get(Dict(:a => 1), :a)
Some(1)

julia> @assert Maybe.get(Dict(:a => 1), :b) === nothing

See more in the documentation.

source
Maybe.TConstant
Maybe.T{T}

A shorthand for Union{Some{T},Nothing}.

source
Maybe.@?Macro
@? expr
@? :debug expr

"Lift" functions to operate on Union{Some{T}, Nothing}.

Idea:

  • Each function f(::T₁, ..., ::Tₙ) -> ::R is (conceptually) lifted to f′(::Union{Some{T₁}, Nothing}, ..., ::Union{Some{Tₙ}, Nothing}) -> ::Union{Some{R}, R, Nothing}.
  • If any of the returned value is nothing, the evaluation of expr in @? short-circuits and evaluates to nothing.

Debugging support (experimental): In the two-argument form @? :debug expr (:debug is the literal symbol :debug), @? inserts @debug logging statements for all possible short-circuiting points. This logging statement prints the expression that is evaluated to nothing and all the local variables using Base.@locals. This macro also inserts a call to a no-op function Maybe._break. When using Debugger.jl, the state just before exit can be examined by adding it to the breakpoint with bp add Maybe._break.

See the tutorial for more information.

Examples

julia> using Maybe

julia> dict = Dict(:a => Dict(:b => 1));

julia> @? dict[:a][:b] + 2
Some(3)

julia> @? dict[:a][:b] + dict[:c]  # => nothing

The expression in @? short-circuits when a call returns nothing:

julia> f() = nothing;

julia> @? println(f())

Some is unwrapped when it appears in the argument position:

julia> f() = Some("hello");

julia> @? println(f())
hello

Non-nothing returned value is always ensured to be wrapped in Some at the end

julia> f() = "hello";

julia> @? f()
Some("hello")

julia> g() = Some("hello");

julia> @? g()
Some("hello")

nothing literal is always wrapped

julia> @? Some(nothing)
Some(nothing)

return $expr does not return when $expr evaluates to nothing

julia> f() = (@? return identity(nothing); return 1);

julia> f()
1

return $expr returns Some($expr):

julia> @? 1 + 1
Some(2)

julia> f() = @? return 1 + 1;

julia> f()
Some(2)

$(...) can be used for switching to the normal evaluation

julia> @? Some(Some(Some(1)))
Some(1)

julia> @? $(Some(Some(Some(1))))
Some(Some(Some(Some(1))))

$f(...) can be used for extra wrapping with Some

julia> f() = Some("hello");

julia> @? f()
Some("hello")

julia> @? $f()
Some(Some("hello"))

@? recursively transforms functions

julia> @? f(dict, i, j, k) =
           get(dict, i) do
               dict[j] + dict[k]
           end;

julia> f(Dict(), :a, :b, :c)

julia> f(Dict(:a => 1), :a, :b, :c)
Some(1)

julia> f(Dict(:b => 1), :a, :b, :c)

julia> f(Dict(:b => 1, :c => 2), :a, :b, :c)
Some(3)
source
Maybe.@somethingMacro
@something(ex₁, ex₂, ..., exₙ)
@something{ex₁; ex₂; ...; exₙ}
@something {ex₁; ex₂; ...; exₙ}

A lazy version of something(x₁, x₂, ..., xₙ). Evaluate exᵢ one by one and return something(result) of the first non-nothing result of exᵢ. Throw an error if everything is evaluated to nothing.

Note

@something{ex₁; ex₂; ...; exₙ} requires Juila >= 1.5.

Examples

julia> using Maybe: @something

julia> @something(
           (println("first"); nothing),
           (println("second"); 2),
           (println("third"); 3),
       )
first
second
2

julia> function maybe_add(xs)
           a = @something(get(xs, 1, nothing), return)
           b = @something(get(xs, 2, nothing), return)
           return a + b
       end;

julia> maybe_add([])

julia> maybe_add([3, 4])
7

julia> @something(nothing, nothing, nothing)
ERROR: ArgumentError: all evaluated as `nothing`
[...]

Since @something is often used with @?, the form @something {ex₁; ex₂; ...; exₙ} can also be used to avoid extra parentheses:

julia> using Maybe: @something, @?

julia> d = Dict(:a => Dict(:b => 1, :c => nothing));

julia> @something {
           @? d[:A][:B];
           @? d[:A][:b];
           @? d[:a][:b];
           @? d[:c][:d];
       }
1

julia> @something(
           (@? d[:A][:B]),
           (@? d[:A][:b]),
           (@? d[:a][:b]),
           (@? d[:c][:d]),
       )
1
source
Maybe.eltypeFunction
Maybe.eltype(itr) -> Some(T::Type) or nothing

Return the element type of iterator if known.

Examples

julia> using Maybe

julia> Maybe.eltype(x + 0 for x in 1:3)

julia> Maybe.eltype(1:0.1:3)
Some(Float64)
source
Maybe.errFunction
Maybe.err(Err(x)) -> Some(x)
Maybe.err(Ok(_)) -> nothing

Return a Some by re-wrapping a value inside an Err result; return nothing on an Ok result.

See also Maybe.ok.

Examples

julia> using Maybe, Try

julia> Maybe.err(Ok(1)) === nothing
true

julia> Maybe.err(Err(1))
Some(1)
source
Maybe.firstFunction
Maybe.first(xs) -> Some(x) or nothing

Try to get the first item x in the container xs and return Some(x) if exists. Return nothing if xs is empty.

Examples

julia> using Maybe

julia> Maybe.first([])

julia> Maybe.first([1])
Some(1)
source
Maybe.getFunction
Maybe.get(d, k) -> Some(x) or nothing

Try to get an item x at key k and return Some(x) if found. Return nothing if not found.

It is more efficient than calling haskey(dict, key) and then dict[key] because it does not lookup the key twice.

Examples

julia> using Maybe

julia> Maybe.get(Dict(), :a)

julia> Maybe.get(Dict(:a => 1), :a)
Some(1)

julia> Maybe.get([1], 2)

julia> Maybe.get([1], 1)
Some(1)
source
Maybe.getindexFunction
Maybge.getindex(xs, indices...) -> Some(x) or nothing

Try to get an item x at location specified by indices and return Some(x) if found. Return nothing if not found.

When xs is an array and Maybge.getindex is called inside @inbounds, it always try to return Some(x) without checking the bounds.

Examples

julia> using Maybe

julia> Maybe.getindex((11, 22, 33), 0)

julia> Maybe.getindex((11, 22, 33), 2)
Some(22)
source
Maybe.getpropertyFunction
Maybe.getproperty(object, name) -> Some(property) or nothing

Return the property named name if object has it.

Examples

julia> using Maybe

julia> Maybe.getproperty((a = 1,), :b)

julia> Maybe.getproperty((a = 1,), :a)
Some(1)
source
Maybe.lastFunction
Maybe.last(xs) -> Some(x) or nothing

Try to get the last item x in the container xs and return Some(x) if exists. Return nothing if xs is empty.

Examples

julia> using Maybe

julia> Maybe.last([])

julia> Maybe.last([1])
Some(1)
source
Maybe.lengthFunction
Maybe.length(itr) -> Some(n::Integer) or nothing

Return the length of iterator if known.

Examples

julia> using Maybe

julia> Maybe.length(x for x in 1:3 if false)

julia> Maybe.length(1:3)
Some(3)
source
Maybe.okFunction
Maybe.ok(Ok(x)) -> Some(x)
Maybe.ok(Err(_)) -> nothing

Return a Some by re-wrapping a value inside an Ok result; return nothing on an Err result.

See also Maybe.err.

Examples

julia> using Maybe, Try

julia> Maybe.ok(Ok(1))
Some(1)

julia> Maybe.ok(Err(1)) === nothing
true
source

Maybe.Extras

Maybe.ExtrasModule
Maybe.Extras

A namespace for extra API; this is for preserving Maybe.* namespace for (mainly) Base-compatible API.

Since there is no name clash with Base API, using Maybe.Extras imports the API defined in Maybe.Extras.

source
Maybe.Extras.getnestedFunction
Maybe.Extras.getnested(x, k₁, k₂, ..., kₙ) -> v::Union{Some{T},Nothing}

Try to get the item v = x[k₁][k₂][...][kₙ] and return Some(v); return nothing if the key does not exist.

source
Maybe.Extras.ifnothingFunction
Maybe.Extras.ifnothing(f) -> x -> ifnothing(f, x)
Maybe.Extras.ifnothing(f, nothing) -> f()
Maybe.Extras.ifnothing(f, Some(x)) -> x

See also defaultto.

Examples

julia> using Maybe.Extras

julia> Some(1) |> ifnothing(() -> :fallback)
1

julia> nothing |> ifnothing(() -> :fallback)
:fallback
source
Maybe.Extras.maybeFunction
maybe(f) -> f′

Transform ("lift") function f(::T₁, ..., ::Tₙ) -> ::Union{Some{R}, R, Nothing} to f′(::Union{T₁, Some{T₁}, Nothing}, ..., ::Union{Tₙ, Some{Tₙ}, Nothing}) -> ::Union{Some{R}, Nothing}.

Examples

julia> using Maybe.Extras

julia> const ⊕ = maybe(+);

julia> 1 ⊕ nothing

julia> 1 ⊕ 2
Some(3)
source