Skip to content

Instantly share code, notes, and snippets.

@grayclhn
Last active July 14, 2021 07:16
Show Gist options
  • Save grayclhn/5e70f5f61d91606ddd93 to your computer and use it in GitHub Desktop.
Save grayclhn/5e70f5f61d91606ddd93 to your computer and use it in GitHub Desktop.
Utility macros and functions for Julia
## Some useful and/or interesting Julia macros. I haven't seen many
## examples online, so I hope this might be useful for people trying
## to understand macros. Caveat Emptor; I don't understand them that
## well myself. Additions and improvements are more than welcome.
##
## Everything here is available under the MIT license (see bottom of
## file)
##
## - Gray Calhoun (@grayclhn) 10/17/2014
using Base.Meta
## Basic functions for working with loops
isloop(e) = false
isloop(e::Expr) = isexpr(e, [:comprehension, :for, :while])
function loophead!(e::Expr, s::Expr)
if isexpr(e, :comprehension)
e.args[2] = s
elseif isexpr(e, [:for, :while])
e.args[1] = s
else
stop("$(e.head) is not supported yet.")
end
end
function loopbody!(e::Expr, s::Expr)
if isexpr(e, :comprehension)
e.args[1] = s
elseif isexpr(e, [:for, :while])
return e.args[2] = s
else
stop("$(e.head) is not supported yet.")
end
end
function loophead(e::Expr)
isexpr(e, :comprehension) && return e.args[2]
isexpr(e, [:for, :while]) && return e.args[1]
stop("$(e.head) is not supported yet.")
end
function loopbody(e::Expr)
isexpr(e, :comprehension) && return e.args[1]
isexpr(e, [:for, :while]) && return e.args[2]
stop("$(e.head) is not supported yet.")
end
## Helper function for the @any and @all macros; this function takes a
## list of expressions and chains them together with the symbol s, so
## chain_exprs(:&&, [:(x == 1), :(y == 2), (z == 3)])
## becomes
## :(x == 1 && y == 2 && z == 3
function chain_exprs(s::Symbol, e...)
length(e) <= 1 && return e[1]
length(e) == 2 && return Expr(s, e[1], e[2])
return make_chained(s, Expr(s, e[1], e[2]), e[3:end]...)
end
## Helper function for the @any and @all macros; this function turns
## an array comprehension or a for loop into a loop that breaks
## _immediately_ when its body returns `value`.
shortcircuit_loop(loop::Expr, value::Bool) =
Expr(:block, :(v = !$value), Expr(:for, loophead(loop), quote
if ($value == $(loopbody(loop)))
v = $value
break
end end), :v)
## Short-circuit `any` macro that ends when the first element is
## true. This is designed to be used in array comprehensions:
## @any [x < 2 for x in 1:1000000000000000000000000000000000]
## should immediately return `true`
macro any(e...)
length(e) >= 2 && return chain_exprs(:||, e...)
isexpr(e[1], :comparison) && return e[1]
isexpr(e[1], :comprehension) && return shortcircuit_loop(e[1], true)
error("@any doesn't support $(e[1].head) yet")
end
## Short-circuit `any` macro that ends when the first element is
## true. This is designed to be used in array comprehensions:
## @all [x < 2 for x in 1:1000000000000000000000000000000000]
## should return `false` in its second check
macro all(e...)
isexpr(e, :comparison) && return e
length(e) >= 2 && return chain_exprs(:&&, e...)
isexpr(e[1], :comprehension) && return shortcircuit_loop(e[1], false)
error("@all doesn't support $(e[1].head) yet")
end
## Copyright (c) 2014 Gray Calhoun
##
## Permission is hereby granted, free of charge, to any person
## obtaining a copy of this software and associated documentation
## files (the "Software"), to deal in the Software without
## restriction, including without limitation the rights to use, copy,
## modify, merge, publish, distribute, sublicense, and/or sell copies
## of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be
## included in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
@NightMachinery
Copy link

all is already shortcircuit-enabled:

julia> all(x < 2 for x in 1:10^30)
false

julia> all([x < 2 for x in 1:10^30])
julia(3922,0x108cf2d40) malloc: can't allocate region
*** mach_vm_map(size=5076944270305267712) failed (error code=3)
julia(3922,0x108cf2d40) malloc: *** set a breakpoint in malloc_error_break to debug
ERROR: OutOfMemoryError()
Stacktrace:
 [1] Array at ./boot.jl:405 [inlined]
 [2] Array at ./boot.jl:414 [inlined]
 [3] similar at ./abstractarray.jl:671 [inlined]
 [4] similar at ./abstractarray.jl:670 [inlined]
 [5] _array_for at ./array.jl:657 [inlined]
 [6] collect(::Base.Generator{UnitRange{Int64},var"#50#51"}) at ./array.jl:670
 [7] top-level scope at REPL[26]:1

julia> all(x < 2 for x in 1:10^30)
false

@grayclhn
Copy link
Author

grayclhn commented Jul 14, 2021

IIRC it wasn't when I wrote this, but this was basically a learning exercise anyway. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment