Skip to content

Commit 7c945df

Browse files
committed
Initial version of SimpleUnPack.jl
1 parent 9a83f06 commit 7c945df

11 files changed

Lines changed: 321 additions & 0 deletions

File tree

.github/workflows/CI.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
concurrency:
8+
# Skip intermediate builds: always.
9+
# Cancel intermediate builds: only if it is a pull request build.
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
12+
jobs:
13+
test:
14+
name: Julia ${{ matrix.version }} - ${{ github.event_name }}
15+
runs-on: ${{ matrix.os }}
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
version:
20+
- '1'
21+
- '1.0'
22+
- '1.6'
23+
- 'nightly'
24+
os:
25+
- ubuntu-latest
26+
arch:
27+
- x64
28+
steps:
29+
- uses: actions/checkout@v3
30+
- uses: julia-actions/setup-julia@v1
31+
with:
32+
version: ${{ matrix.version }}
33+
arch: ${{ matrix.arch }}
34+
- uses: julia-actions/cache@v1
35+
- uses: julia-actions/julia-buildpkg@v1
36+
- uses: julia-actions/julia-runtest@v1
37+
- uses: julia-actions/julia-processcoverage@v1
38+
- uses: codecov/codecov-action@v3
39+
with:
40+
files: lcov.info
41+
- uses: julia-actions/julia-uploadcoveralls@v1
42+
env:
43+
COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}

.github/workflows/CompatHelper.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CompatHelper
2+
on:
3+
schedule:
4+
- cron: 0 0 * * *
5+
workflow_dispatch:
6+
jobs:
7+
CompatHelper:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Pkg.add("CompatHelper")
11+
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
12+
- name: CompatHelper.main()
13+
env:
14+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
15+
COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }}
16+
run: julia -e 'using CompatHelper; CompatHelper.main()'

.github/workflows/Format.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Format
2+
on:
3+
push:
4+
branches:
5+
- main
6+
pull_request:
7+
concurrency:
8+
# Skip intermediate builds: always.
9+
# Cancel intermediate builds: only if it is a pull request build.
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
12+
jobs:
13+
format:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v3
17+
- uses: julia-actions/setup-julia@v1
18+
with:
19+
version: 1
20+
- name: Format code
21+
run: |
22+
using Pkg
23+
Pkg.add(; name="JuliaFormatter", uuid="98e50ef6-434e-11e9-1051-2b60c6c9e899")
24+
using JuliaFormatter
25+
format("."; verbose=true)
26+
shell: julia --color=yes {0}
27+
- uses: reviewdog/action-suggester@v1
28+
if: github.event_name == 'pull_request'
29+
with:
30+
tool_name: JuliaFormatter
31+
fail_on_error: true

.github/workflows/TagBot.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name: TagBot
2+
on:
3+
issue_comment:
4+
types:
5+
- created
6+
workflow_dispatch:
7+
jobs:
8+
TagBot:
9+
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: JuliaRegistries/TagBot@v1
13+
with:
14+
token: ${{ secrets.GITHUB_TOKEN }}
15+
ssh: ${{ secrets.DOCUMENTER_KEY }}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.jl.*.cov
2+
*.jl.cov
3+
*.jl.mem
4+
/Manifest.toml
5+
/docs/build/

JuliaFormatter.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
style = "blue"

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 David Widmann
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Project.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name = "SimpleUnPack"
2+
uuid = "ce78b400-467f-4804-87d8-8f486da07d0a"
3+
authors = ["David Widmann"]
4+
version = "1.0.0"
5+
6+
[compat]
7+
julia = "1"
8+
9+
[extras]
10+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
11+
12+
[targets]
13+
test = ["Test"]

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# SimpleUnPack
2+
3+
[![Build Status](https://github.com/devmotion/SimpleUnPack.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/devmotion/SimpleUnPack.jl/actions/workflows/CI.yml?query=branch%3Amain)
4+
[![Coverage](https://codecov.io/gh/devmotion/SimpleUnPack.jl/branch/main/graph/badge.svg)](https://codecov.io/gh/devmotion/SimpleUnPack.jl)
5+
[![Coverage](https://coveralls.io/repos/github/devmotion/SimpleUnPack.jl/badge.svg?branch=main)](https://coveralls.io/github/devmotion/SimpleUnPack.jl?branch=main)
6+
[![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/invenia/BlueStyle)
7+
8+
This package provides the `@unpack` macro for destructuring properties.
9+
Its behaviour is equivalent to the destructuring that was introduced in [Julia#39285](https://github.com/JuliaLang/julia/pull/39285) and is available in Julia >= 1.7.0-DEV.364.
10+
11+
## Examples
12+
13+
An example with `NamedTuple` in global scope:
14+
15+
```julia
16+
julia> using SimpleUnPack
17+
18+
julia> f(x) = (; b=x, a=x/2);
19+
20+
julia> @unpack a, b = f(42)
21+
(b = 42, a = 21.0)
22+
23+
julia> a
24+
21.0
25+
26+
julia> b
27+
42
28+
```
29+
30+
An example with a custom struct in a function:
31+
32+
```julia
33+
julia> using SimpleUnPack
34+
35+
julia> struct MyStruct{T}
36+
x::T
37+
end
38+
39+
julia> Base.getpropertynames(::MyStruct) = (:x, :y)
40+
41+
julia> function Base.getproperty(m::MyStruct, p::Symbol)
42+
if p === :y
43+
return 42
44+
else
45+
return getfield(m, p)
46+
end
47+
end
48+
49+
julia> function g(m::MyStruct)
50+
@unpack x, y = m
51+
return (; x, y)
52+
end;
53+
54+
julia> g(MyStruct(1.0))
55+
(x = 1.0, y = 42)
56+
```
57+
58+
## Comparison with UnPack.jl
59+
60+
The syntax of `@unpack` is based on [`UnPack.@unpack`](https://github.com/mauro3/UnPack.jl).
61+
However, `UnPack.@unpack` is more flexible and based on `UnPack.unpack` instad of `getproperty`.
62+
While `UnPack.unpack` falls back to `getproperty`, it also supports `AbstractDict`s with keys of type `Symbol` and `AbstractString`, and can be specialized for other types.
63+
Since `UnPack.unpack` dispatches on `Val(property)` instances, this increased flexibility comes at the cost of increased compilation times.
64+
Moreover, UnPack also provides an `UnPack.@pack!` macro for setting properties.

src/SimpleUnPack.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module SimpleUnPack
2+
3+
export @unpack
4+
5+
"""
6+
@unpack a, b, ... = rhs
7+
8+
Destructure properties `a`, `b`, ... of `rhs` into variables of the same name.
9+
10+
The behaviour of the macro is equivalent to `(; a, b, ...) = rhs` which was introduced in [Julia#39285](https://github.com/JuliaLang/julia/pull/39285) and is available in Julia >= 1.7.0-DEV.364.
11+
"""
12+
macro unpack(args::Expr)
13+
return unpack(args)
14+
end
15+
16+
function unpack(args::Expr)
17+
# Extract properties and RHS
18+
if !Meta.isexpr(args, :(=), 2)
19+
throw(ArgumentError("`@unpack` can only be applied to expressions of the form `a, b = c`"))
20+
end
21+
lhs, rhs = args.args
22+
properties = if lhs isa Symbol
23+
[lhs]
24+
elseif Meta.isexpr(lhs, :tuple) && !isempty(lhs.args) && all(x -> x isa Symbol, lhs.args)
25+
lhs.args
26+
else
27+
throw(ArgumentError("`@unpack` can only be applied to expressions of the form `a, b = c`"))
28+
end
29+
30+
if VERSION >= v"1.7.0-DEV.364"
31+
# Fall back to destructuring in Base when available:
32+
# https://github.com/JuliaLang/julia/pull/39285
33+
return Expr(:(=), Expr(:tuple, Expr(:parameters, (esc(p) for p in properties)...)), esc(rhs))
34+
else
35+
@gensym object
36+
block = Expr(:block)
37+
for p in properties
38+
push!(block.args, Expr(:(=), esc(p), Expr(:call, :getproperty, esc(object), QuoteNode(p))))
39+
end
40+
return quote
41+
$(esc(object)) = $(esc(rhs)) # In case the RHS is an expression
42+
$block
43+
$(esc(object)) # Return evaluation of rhs to ensure the behaviour is the same as (; ...) = rhs
44+
end |> Base.remove_linenums!
45+
end
46+
end
47+
48+
end

0 commit comments

Comments
 (0)