简体   繁体   中英

Isn't there a nicer way to combine multiple `Union{T, Nothing}`

I'm very new to Julia but I've got a some background in Scheme/Rust/F#.

Today I wanted to make yesterday's AoC nicer without an explicit number of nested loops.

I arrived at this working solution, but I don't like the last if . In the languages mentioned above I would call a function (or use a computation expression) that gives me the first result that is not None . For Julia, I expected something to do that. It does , but unexpectedly in an eager fashion.

So When I tried return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended)) , that also evaluated the second argument when the first already had a result—and thus crashed.

Is there a macro/lazy version or something that I didn't find? How are you supposed to handle a case like that?

I also thought about (short-circuited) or'ing them together, but I guess Julia's strictness in that matter spoils that.

using DataStructures

function find(r::Array{Int}, n, start = 1, which = nil())::Union{Int,Nothing}
    if start <= length(r)
        extended = cons(start, which)
        with_current = sum(i -> r[i], extended)
        if with_current == 2020 && n == 1
            return prod(i -> r[i], extended)
        else
            # Unfortunately no :(
            #return something(find(r, n, start + 1, which), find(r, n - 1, start + 1, extended))
            re = find(r, n, start + 1, which)
            if isnothing(re)
                return find(r, n - 1, start + 1, extended)
            else
                re
            end
        end
    end
end

Let me comment more on it why it is not possible given the discussion in the comments.

In Julia function arguments are evaluated eagerly, so Julia evaluates both find(r, n, start + 1, which) and find(r, n - 1, start + 1, extended) before passing them to something function.

Now, with macros you have (I am not writing in a fully general case for simplicity and I hope I got the hygiene right:)):

julia> macro something(x, y)
           quote
               local vx = $(esc(x))
               isnothing(vx) ? $(esc(y)) : vx
           end
       end
@something (macro with 1 method)

julia> @something 1 2
1

julia> @something nothing 2
2

julia> @something 1 sqrt(-1)
1

julia> @something nothing sqrt(-1)
ERROR: DomainError with -1.0:
sqrt will only return a complex result if called with a complex argument. Try sqrt(Complex(x)).

(in a full-blown version of the macro varargs and Some should be handled to replicate something exactly)

Piqued by Bogumił's answer I wanted to write my first Julia macro. It took some time and numerous attempts to figure out syntax, hygiene and escaping but I'm quite happy now.

I thought it might be worth sharing and provide opportunity for suggestions/improvements.

A lazy @something analog to Base.something

function _something_impl(thing)
    :(something($(esc(thing))))
end

function _something_impl(thing, rest...)
    quote
        local evalued = $(esc(thing))
        if isnothing(evalued)
            $(_something_impl(rest...))
        else
            something(evalued)
        end
    end
end

macro something(things...)
    _something_impl(things...)
end

Version without exceptions

As I found exceptions raised from a macro like this not quite suitable, I also made a version that falls back to nothing .

function _something_nothing_impl(thing)
    quote
        local evaluated = $(esc(thing))
        if isa(evaluated, Some)
            evaluated.value
        else
            evaluated
        end
    end
end

function _something_nothing_impl(thing, rest...)
    quote
        local evalued = $(esc(thing))
        if isnothing(evalued)
            $(_something_nothing_impl(rest...))
        else
            something(evalued)
        end
    end
end

macro something_nothing(things...)
    _something_nothing_impl(things...)
end

Now I guess the recursive middle function could also generated by a macro. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM