It turns out something(Some(2), do_work(3)) still invoke do_work, which returns 2 seconds later.
It would be better to define it as a macro, which retains the arguments as AST and only evaluate them when necessary.
This is simply eager evaluation. If you really need something to behave like this, you need to use a macro. Alternatively, && and || already short-circuits, so if it does no side effects, you can consider using those too.
macro something()
return :(throw(ArgumentError("No value arguments present")))
end
macro something(head, rest...)
expr = quote
if $head isa Nothing
return # somehow insert expansion of @something($(rest...)) at this position
else
return $head
end
end
return expr
end
I wonder if it is even possible to make such a macro.
It’s possible: macros are arbitrary functions which transform syntax into syntax. One fairly direct way to write it could be as follows (there’s shorter ways but this should be relatively easy to understand):
function something_expr(ex, exs...)
quote
s = $(esc(ex))
if isnothing(s)
$(something_expr(exs...))
else
s
end
end
end
something_expr(ex) = :(something(ex))
macro something(exs...)
something_expr(exs...)
end
Thank you very much! There is a error in the base case. Here is the complete code:
# https://discourse.julialang.org/t/something-a-b-not-shortcutting/27746/4.
# All credits to Chris Foster
function something_expr(head, rest...)
quote
head_val = $(esc(head))
if head_val isa Some
head_val.value
elseif isnothing(head_val)
$(something_expr(rest...))
else
head_val
end
end
end
function something_expr(head)
quote
something($(head))
end
end
macro something(args...)
something_expr(args...)
end
# test
function f(x)
sleep(1)
x
end
@something 1 2 # 1
@something 1 f(2) # 1, without delay
@something Some(1) 2 # 1
@something nothing 2 # 2
@something sleep(1) 2 # 2, 1s delay