# Unpack named tuples (does anyone know how to do it?)

**URL:** <https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456>\
**Category:** General Usage\
**Created:** [December 18, 2019, 10:15pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456 "2019-12-18T22:15:44Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![rvignolo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rvignolo/32/33498_2.png) [@rvignolo](https://discourse.julialang.org/u/rvignolo)\
**Post date:** [December 18, 2019, 10:15pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/1 "2019-12-18T22:15:44Z")

</div>

Hi all,

I just wanted to know if it is possible to unpack named tuples in the following context. I know that `Parameters.jl` addresses some related issues, but I have not been able to figure out if it can handle the following case. Define a named tuple using `@with_kw`:

```nohighlight
ParamsNamedTuple = @with_kw (
  T = 1.0,
  K = 0.5,
  M = T + K,
  v = zeros(2),
  d = 0.1,
  t = [i * d for i in 0:10]
)

```

We can unpack particular components of a particular instance using:

```nohighlight
julia> params = ParamsNamedTuple();
julia> @unpack T, K = params;
julia> T
1.0
julia> K
0.5
julia> M
ERROR: UndefVarError: M not defined

```

However, I would like to unpack them all:

```nohighlight
@unpack params

```

Is this possible?

On the other hand, I know that we can unpack all the objects if instead of defining a named tuple we define a struct, such as:

```nohighlight
@with_kw struct ParamsStruct{T1, T2, T3, T4, T5, T6}
  T::T1 = 1.0
  K::T2 = 0.5
  M::T3 = T + K
  v::T4 = zeros(2)
  d::T5 = 0.1
  t::T6 = [i * d for i in 0:10]
end

```

Then:

```nohighlight
params = ParamsStruct()
@unpack_ParamsStruct params

```

Any unpacking strategy would be ok. There is no need to use a macro in `Parameters.jl` or any other package.

Thank you!

---

<div class="post-metadata">

**Author:** ![longemen3000](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/longemen3000/32/7298_2.png) [@longemen3000](https://discourse.julialang.org/u/longemen3000)\
**Post date:** [December 19, 2019, 3:39am UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/2 "2019-12-19T03:39:49Z")

</div>

you need to know ahead of time the number of values if you gonna assign them. but extracting the values of a `NamedTuple` can be done via the function `values`

```julia
tup = (a=3,b=5) #example named tuple
tup_values = values(tup) # (3,5)
a,b = values(tup) #a = 3, b = 5, two variables defined

```

this approach requires assigning the variable names manually, if you want that automatically (i do not recommend this, as it can collide with any defined variable), maybe this macro can help:

```julia
function _unpack(x::NamedTuple)
    kk = keys(x)
    vv = values(x)
    i = 1
    for k in kk
        @eval $k = $vv[$i]
        i +=1
    end
end

macro unpack_namedtuple(arg)
 quote
    _unpack($arg)
end |> esc
end

tup = (a=1,b=2)
@unpack_namedtuple tup #a and b are now defined in the scope

```

this can fail in this case, for example:

```julia
tup = (tup=2,b=3)
@unpack_namedtuple tup
julia>tup #the tuple is lost
2

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [December 19, 2019, 5:15am UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/3 "2019-12-19T05:15:51Z")

</div>

The `@eval` solution also only works at global scope:

```julia
julia> function foo()
         tup = (a = 1, b=2)
         @unpack_namedtuple tup
         a = a + 1
       end
foo (generic function with 1 method)

julia> foo()
ERROR: UndefVarError: a not defined
Stacktrace:
 [1] foo() at ./REPL[6]:4
 [2] top-level scope at REPL[7]:1

```

(this fails because the `a` created by the macro is a global variable).

To the original question, I think the answer is no: there’s no efficient way to do this. You have to, somewhere, somehow, specify the set of local variables you want to create. A macro can’t do anything magical: it just makes it easier to write out code that you could otherwise write out by hand. If there’s no way to write the code by hand, then there’s no way to implement it via a macro.

Unpacking a subset of fields with `@unpack a, b = params` works because by listing `a, b = params` you are providing enough information for the macro to create code like:

```julia
a = params.a
b = params.b

```

The `@unpack_ParamsStruct` call works because that macro was defined automatically when you ran `@with_kw`, which looked at your struct definition and picked out all of the field names.

But what would the generated code be for the hypothetical `@unpack params`? A macro just takes some code and produces other code. The input to the macro is just the symbol `:params`. How could the macro know that it should unpack `params` into fields `a`, `b`, and `c`?

---

<div class="post-metadata">

**Author:** ![rvignolo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rvignolo/32/33498_2.png) [@rvignolo](https://discourse.julialang.org/u/rvignolo)\
**Post date:** [December 19, 2019, 1:02pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/7 "2019-12-19T13:02:08Z")

</div>

Hi @rdeits, thanks for your detailed answer!

> [@rdeits](#):
>
> But what would the generated code be for the hypothetical `@unpack params` ?

That is exactly the question. However, this was just syntax, it was not the proposed solution. I know that the input to the macro is just the symbol `:params`. I though that the macro may return something like @longemen3000 suggested. Actually, I thought maybe the macro was already coded in some package and I wanted to know in which one 😄.

Finally, as I understand, a solution may be the same one in `Parameters.jl` for `struct`s, i.e., create a macro `@unpack_ParamsNamedTuple` on the fly.

I have already raised an issue in `Parameters.jl` but I am still waiting for @mauro3 response 😀.

Thanks and regards!

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 19, 2019, 3:01pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/8 "2019-12-19T15:01:34Z")

</div>

> [@rvignolo](#):
>
> create a macro `@unpack_ParamsNamedTuple` on the fly.

If you have a specific `NamedTuple` type that you use an alias for, you might as well just define `struct`, they are much nicer for dispatch etc.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [December 19, 2019, 3:08pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/9 "2019-12-19T15:08:41Z")

</div>

> [@rvignolo](#):
>
> I though that the macro may return something like @longemen3000 suggested.

That’s totally reasonable to hope for, but it won’t work precisely because the suggested code, even if written out by hand, still doesn’t work (it creates global variables, not local ones, because that is inherently how `@eval` always works).

The only way for `@unpack params` to work would be for the type of `params` to be known when the `@unpack` macro is expanded, but that’s impossible: if you’re inside a function, the macro will be expanded before the function is run and therefore before `params` has a value.

---

<div class="post-metadata">

**Author:** ![rvignolo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rvignolo/32/33498_2.png) [@rvignolo](https://discourse.julialang.org/u/rvignolo)\
**Post date:** [December 19, 2019, 3:21pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/10 "2019-12-19T15:21:25Z")

</div>

> [@rdeits](#):
>
> That’s totally reasonable to hope for, but it won’t work precisely because the suggested code, even if written out by hand, still doesn’t work (it creates global variables, not local ones, because that is inherently how `@eval` always works).

This is correct!

> [@rdeits](#):
>
> The only way for `@unpack params` to work would be for the type of `params` to be known when the `@unpack` macro is expanded, but that’s impossible: if you’re inside a function, the macro will be expanded before the function is run and therefore before `params` has a value.

But building a specific macro on the fly will solve this issue, right?

---

<div class="post-metadata">

**Author:** ![rvignolo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rvignolo/32/33498_2.png) [@rvignolo](https://discourse.julialang.org/u/rvignolo)\
**Post date:** [December 19, 2019, 3:22pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/11 "2019-12-19T15:22:33Z")

</div>

Hi @Tamas_Papp, thanks for your reply.

You may find useful [this](https://github.com/mauro3/Parameters.jl/issues/114) discussion in order to see why I would like to have named tuples instead of structs.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 19, 2019, 3:26pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/12 "2019-12-19T15:26:02Z")

</div>

I am still not sure if I understand why you can’t use `struct`s, even after reading that issue, but as suggested there you can just have the user pass functions.

The fundamental difficulty with a macro is that macros operate on _syntax_, while (in general) the fields depend on the type of a _value_. There are ways of jumping around these two domains with `eval`, but usually that ends up being the least idiomatic solution for a problem.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [June 14, 2020, 5:16am UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/13 "2020-06-14T05:16:39Z")

</div>

I started thinking about this recently after seeing [this thread](https://discourse.julialang.org/t/with-struct-do/41341/7). I mostly agree with @Tamas_Papp that a struct is likely the best solution, however I think these sorts of code transformations are interesting to think about.

Basically, what would be needed to do this is a macro that knows about type information. Base Julia does not have such a construct, but IRTools.jl sorta does with it’s `@dynamo`.

Here is a dynamo I call `with` that in some senses will behave like `@unpack`, even on a named Tuple:

```julia
using IRTools: IRTools, @dynamo, @code_ir, postwalk, prewalk, IR, argument!, 
using IRTools: xcall, var, insertafter!, isexpr, branches, blocks
using Setfield: @set

@dynamo function with(f, obj)
    ir = IR(f)
    objarg = argument!(ir)
    d = Dict()
    for fn ∈ reverse(fieldnames(obj))
        d[fn] = insertafter!(ir, var(1), xcall(:getfield, objarg, QuoteNode(fn)))
    end

    function replacer(x)
        if x isa GlobalRef && x.name ∈ fieldnames(obj)
            d[x.name]
        else
            x
        end
    end

    for (n, st) ∈ ir
        ir[n] = prewalk(replacer, st)
    end

    for blck ∈ blocks(ir)
        bs = branches(blck)
        for i ∈ eachindex(bs)
            b = bs[i]
            bs[i] = @set b.condition = postwalk(replacer, b.condition)
            for j ∈ eachindex(bs[i].args) 
                bs[i].args[j] = postwalk(replacer, bs[i].args[j])
            end
        end
        ir
    end
    ir
end

```

Apologies for the spaghetti code, I’m not yet very skilled with writing IR. Here’s `with` in action:

```julia
julia> nt = (;a=1, b=4.0, c="hi", d=true)
(a = 1, b = 4.0, c = "hi", d = true)

julia> with(nt) do
           if !d
               (a - b)/(a + b)
           else
               c
           end
       end
"hi"

```

This is a rather brittle solution that has many weaknesses.

For instance, if there’s a local variable available named `d`, then `d` won’t get replaced with `nt.d`:

```julia
julia> let
           nt = (;a=1, b=4.0, c="hi", d=true)
           d = false
           with(nt) do
               if !d
                   (a - b)/(a + b)
               else
                   c
               end
           end
       end
-0.6

```

I’m sure there are other problems and corner cases, but I learned a lot writing this so I thought I’d share.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [June 18, 2022, 5:38pm UTC](https://discourse.julialang.org/t/unpack-named-tuples-does-anyone-know-how-to-do-it/32456/14 "2022-06-18T17:38:15Z")

</div>

btw

> [@Destructuring syntax in julia 1.7](https://discourse.julialang.org/t/destructuring-syntax-in-julia-1-7/68236):
>
> 1.7 introduced new language feature: (; a, b) = x can now be used to destructure properties a and b of x . This syntax is equivalent to a = getproperty(x, :a); b = getproperty(x, :b) From a reader perspective(much more compare to writer perspective in action), I can not appreciate the elegance of this syntax. It’s too not “mathematical”. Existing argument destructuring for tuple is much elegant as there is an isomorphism between the delcare structure and the data structure. Why this new synt…
