Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates #113

Merged
merged 4 commits into from
Dec 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 45 additions & 33 deletions src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ using MacroTools

import Base: replace

export @>, @>>, @as, @_, @switch, @or, @dotimes, @oncethen, @defonce, @with, @errs,
export @>, @>>, @as, @switch, @or, @dotimes, @oncethen, @defonce, @with, @errs,
@forward, @iter

"""
Expand Down Expand Up @@ -69,26 +69,26 @@ function splitswitch(ex)
end

"""
The threading macro is like a more flexible version of the `|>` operator.
The threading macro is like a more flexible version of the [`|>`](@ref) operator.

@> x f = f(x)
@> x g f == f(g(x))
@> x a b c d e == e(d(c(b(a(x)))))

Unlike |>, functions can have arguments - the value
Unlike [`|>`](@ref), functions can have arguments - the value
preceding a function will be treated as its first argument

@> x g(y, z) f == f(g(x, y, z))

@> x g f(y, z) == f(g(x), y, z)

See also `@>>`, `@as`.
See also [`@>>`](@ref), [`@as`](@ref).
"""
macro >(exs...)
thread(x) = isexpr(x, :block) ? thread(rmlines(x).args...) : x

@static if VERSION < v"0.7"

thread(x, ex) =
isexpr(ex, :call, :macrocall) ? Expr(ex.head, ex.args[1], x, ex.args[2:end]...) :
@capture(ex, f_.(xs__)) ? :($f.($x, $(xs...))) :
Expand All @@ -107,16 +107,18 @@ macro >(exs...)
end

thread(x, exs...) = reduce(thread, exs, init=x)

esc(thread(exs...))
end

"""
Same as `@>`, but threads the last argument.
Same as [`@>`](@ref), but threads the last argument.

@>> x g(y, z) f == f(g(y, z, x))

@>> x g(y, z) f == f(g(y, z, x))
@>> x g f(y, z) == f(y, z, g(x))

@>> x g f(y, z) == f(y, z, g(x))
See also: [`@>>`](@ref)
"""
macro >>(exs...)
thread(x) = isexpr(x, :block) ? thread(rmlines(x).args...) : x
Expand All @@ -134,17 +136,20 @@ macro >>(exs...)
end

"""
# @as lets you name the threaded argmument
@as _ x f(_, y) g(z, _) == g(z, f(x, y))
@as as, exs...

# All threading macros work over begin blocks
`@as` lets you name the threaded argmument

@as x 2 begin
x^2
x+2
end == 6
@as _ x f(_, y) g(z, _) == g(z, f(x, y))

`@_` is a version of `@as` which defaults to `_` as the argument name.
All threading macros work over begin blocks

```julia
6 === @as x 2 begin
x^2
x+2
end
```
"""
macro as(as, exs...)
thread(x) = isexpr(x, :block) ? thread(rmlines(x).args...) : x
Expand Down Expand Up @@ -181,7 +186,11 @@ macro or(exs...)
thread(exs...)
end

"Repeat `body` `n` times."
"""
@dottimes(n, body)

Repeat `body` `n` times.
"""
macro dotimes(n, body)
quote
for i = 1:$(esc(n))
Expand All @@ -191,7 +200,7 @@ macro dotimes(n, body)
end

"""
A do-while loop – executes the while loop once regardless of the
A `do`-`while` loop – executes the `while` loop once regardless of the
condition, then tests the condition before subsequent iterations.
"""
macro oncethen(expr::Expr)
Expand All @@ -203,24 +212,25 @@ macro oncethen(expr::Expr)
end

"""
Stop Julia from complaining about redifined consts/types
Stop Julia from complaining about redifined struct/consts

@defonce type MyType
@defonce struct MyType
...
end
or

or

@defonce const pi = 3.14
"""
macro defonce(def)
name = namify(isexpr(def, :type) ? def.args[2] : def)

:(if !isdefined($(Expr(:quote, name)))
$(esc(def))
end)
name = namify(isexpr(def, :struct) ? def.args[2] : def)
if !isdefined(__module__, name)
return :($(esc(def)))
end
end

"""
End-less let block, e.g.
End-less `let` block, e.g.

@with (x = 1, y = 2),
x+y
Expand All @@ -241,12 +251,14 @@ s(xs...) = Set{Any}(xs)
d(xs...) = Dict{Any, Any}(xs...)

"""
Creates a typed dictionary, e.g.
Creates an **un**typed dictionary, e.g.

julia> @d(a=>1,b=>2)
Dict{Any,Any} with 2 entries:
:a => 1
:b => 2
```julia
julia> @d(:a=>1, :b=>2)
Dict{Any,Any} with 2 entries:
:a => 1
:b => 2
```
"""
macro d(xs...)
:(Dict{Any, Any}($(map(esc, xs)...)))
Expand Down
13 changes: 6 additions & 7 deletions src/tail.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,14 @@ Without `@rec` this function would overflow the stack for
lists of 80,000 or more elements.

Caveats:

• No support for trampolining, i.e. only calls to the
- No support for trampolining, i.e. only calls to the
given function are optimised away.
Ignores multiple dispatch – it is assumed that the function's
- Ignores multiple dispatch – it is assumed that the function's
name always refers to the given definition.
Don't rebind the function's name in a let (see above).
Don't use this with varargs functions.
- Don't rebind the function's name in a let (see above).
- Don't use this with varargs functions.

Use the more flexible, but slower, `@bounce` to avoid these issues.
Use the more flexible, but slower, [`@bounce`](@ref) to avoid these issues.
"""
macro rec(def)
def = shortdef(macroexpand(@__MODULE__, def))
Expand Down Expand Up @@ -114,7 +113,7 @@ Tail recursion that doesn't blow the stack.
even(1_000_000) # Blows up without `@bounce`.
#> true

For simple cases you probably want the much faster `@rec`.
For simple cases you probably want the much faster [`@rec`](@ref).
"""
macro bounce(def)
def = macroexpand(@__MODULE__, def)
Expand Down