# What's your preference for creating literal data-structures?

**URL:** https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128
**Category:** General Usage
**Tags:** style
**Created:** [February 20, 2025, 9:26pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128 "2025-02-20T21:26:14Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![jballanc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jballanc/32/328_2.png) [@jballanc](https://discourse.julialang.org/u/jballanc)
#### Post date: [February 20, 2025, 9:26pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/1 "2025-02-20T21:26:14Z")

</div>

Having worked fairly extensively with Clojure, I came to appreciate the approach of building systems focused around data vs code. For example, if you are working on a business rules engine, rather than writing a complex collection of functions and structs that describe and execute the rules, it’s better to use a simple collection of composable functions that can operate on a data structure and let the data structure describe the rules. The major advantage of this approach is that there are many more tools to work with vanilla data types (composing, splitting, iterating, etc.) than there are with a custom collection of functions & types.

The only problem I’m coming across with Julia (as compared to Clojure) is that I haven’t been able to settle on a nice, clean, ergonomic approach for constructing large, complex data literals. For example, the following code in Clojure constructs a top-level dictionary with nested dictionaries that have a mix of vectors, strings, and number literals for values:

```clojure
{ :foo { :bar [1 2 3]
         :baz "Hello, world"
         :qux 9000 }
  :bohica { :bar [4 5 6]
            :baz "Goodnight, moon"
            :qux 2000 } }

```

So, what’s the best way to do the same in Julia? One option:

```julia
Dict(:foo => Dict(:bar => [1, 2, 3],
                  :baz => "Hello, world",
                  :qux => 9000),
     :bohica => Dict(:bar => [4, 5, 6],
                     :baz => "Goodnight, moon",
                     :qux => 2000))

```

feels just a bit too verbose (especially if, as I’m looking to do, one wants to nest `Dict`s within `Dict`s within `Dict`s). An alternative I’ve explored is to, instead, construct the data as a matrix and then later (via function or macro, doesn’t particularly matter) convert anything with `eltype` of `Pair` into a dict:

```julia
[:foo => [:bar => [1 2 3]
          :baz => "Hello, world"
          :qux => 9000]
 :bohica => [:bar => [4 5 6]
             :baz => "Goodnight, moon"
             :qux => 2000]]

```

This is only just a bit more verbose than Clojure, which I like, but I wonder if using data literals like this is too alien to the Julia community. Thoughts?

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 20, 2025, 9:50pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/2 "2025-02-20T21:50:28Z")

</div>

Have you thought about named tuples?

```julia
(; foo = (; bar=[1,2,3], baz="Hello, world", qux=9000),
   bohica = (; bar=[4,5,6], baz="Goodnight, moon", qux=2000))

```

is pretty even more compact than your Clojure example (and doesn’t need `:` quoting).

Depends on what you want to do with it and whether you require mutability, of course. But since this is valid Julia syntax, you could easily have a `@dict` macro that constructs nested `Dict`s from the same syntax.

---

<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: [February 20, 2025, 9:55pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/3 "2025-02-20T21:55:07Z")

</div>

> [@stevengj](#):
>
> ```julia
> (; foo = (; bar=[1,2,3], baz="Hello, world", qux=9000),
> bohica = (; bar=[4,5,6], baz="Goodnight, moon", qux=2000))
> 
> ```
> 
> is pretty even more compact than your Clojure example (and doesn’t need `:` quoting).

You can get this even a little more compact by omitting the not-strictly-needed `;`:

```julia
(foo = (bar=[1,2,3], baz="Hello, world", qux=9000),
 bohica = (bar=[4,5,6], baz="Goodnight, moon", qux=2000))

```

---

<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: [February 20, 2025, 10:02pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/4 "2025-02-20T22:02:32Z")

</div>

FWIW, I make heavy use of `NamedTuple`s and `NamedTuple`-like structs for things like this all the time. It’s a different tool than a dictionary, but for the sorts of things I need stuff like this, it’s almost always a nicer tool (not just because of the syntax)

If you do find yourself wanting a nice synax for dictionaries though, you can always write a macro:

```julia
macro d(ex)
    @assert ex.head == :braces
    dargs = map(ex.args) do arg::Expr
        @assert arg.head == :call
        @assert arg.args[1] == :(:)
        @assert length(arg.args) == 3
        lhs = esc(arg.args[2])
        rhs = esc(arg.args[3])
        :($lhs => $rhs)
    end
    :(Dict($(dargs...)))
end

```

```julia-repl
julia> @d{:foo : @d{:bar : [1 2 3],
                    :baz : "Hello, world",
                    :qux : 9000},
          :bohica : @d{:bar : [4 5 6],
                       :baz : "Goodnite, moon",
                       :qux : 2000}}
Dict{Symbol, Dict{Symbol, Any}} with 2 entries:
  :bohica => Dict(:baz=>"Goodnite, moon", :bar=>[4 5 6], :qux=>2000)
  :foo => Dict(:baz=>"Hello, world", :bar=>[1 2 3], :qux=>9000)

```

---

<div class="post-metadata">

### Author: ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)
#### Post date: [February 20, 2025, 10:07pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/5 "2025-02-20T22:07:35Z")

</div>

Is there a way to only require the macro at the outermost level?

---

<div class="post-metadata">

### Author: ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)
#### Post date: [February 20, 2025, 10:08pm UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/6 "2025-02-20T22:08:01Z")

</div>

> [@stevengj](#):
>
> Depends on what you want to do with it and whether you require mutability, of course. But since this is valid Julia syntax, you could easily have a `@dict` macro that constructs nested `Dict`s from the same syntax.

For example, with

```julia
_dict(x) = x
function _dict(ex::Expr)
    if Meta.isexpr(ex, :tuple) && !isempty(ex.args) && (Meta.isexpr(ex.args[1], :parameters) || Meta.isexpr(ex.args[1], :(=)))
        kws = Meta.isexpr(ex.args[1], :parameters) ? ex.args[1].args : ex.args
        Expr(:call, :Dict, map(kws) do arg
            :($(QuoteNode(arg.args[1])) => $(_dict(arg.args[2])))
        end...)
    else
        return ex
    end
end
macro dict(expr)
    return esc(_dict(expr))
end

```

You get:

```julia
julia> @dict (foo = (bar=[1,2,3], baz="Hello, world", qux=9000),
              bohica = (bar=[4,5,6], baz="Goodnight, moon", qux=2000))
Dict{Symbol, Dict{Symbol, Any}} with 2 entries:
  :bohica => Dict(:baz=>"Goodnight, moon", :bar=>[4, 5, 6], :qux=>2000)
  :foo => Dict(:baz=>"Hello, world", :bar=>[1, 2, 3], :qux=>9000)

```

(You might want to use PropDicts.jl for this instead of `Dict`)

> [@DNF](#):
>
> Is there a way to only require the macro at the outermost level?

(Yes, see above for example.)

---

<div class="post-metadata">

### Author: ![jballanc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jballanc/32/328_2.png) [@jballanc](https://discourse.julialang.org/u/jballanc)
#### Post date: [February 21, 2025, 2:42am UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/7 "2025-02-21T02:42:45Z")

</div>

> [@stevengj](#):
>
> Have you thought about named tuples?

Ah, yes, I had actually looked at named tuples, but from a little bit of benchmarking I was concerned by access speed. Constructing the tuples is _way_ faster than constructing the `Dict`s, but access to nested members is 2x slower:

```julia
julia> a = @btime Dict(:foo => Dict(:bar => [1, 2, 3], :baz => "Hello, world", :qux => 9000), :bohica => Dict(:bar => [4, 5, 6], :baz => "Goodnight, moon", :qux => 2000))
  678.212 ns (35 allocations: 2.78 KiB)
Dict{Symbol, Dict{Symbol, Any}} with 2 entries:
  :bohica => Dict(:baz=>"Goodnight, moon", :bar=>[4, 5, 6], :qux=>2000)
  :foo => Dict(:baz=>"Hello, world", :bar=>[1, 2, 3], :qux=>9000)
julia> @btime a[:foo][:bar]
  41.901 ns (0 allocations: 0 bytes)
julia> b = @btime (foo=(bar=[1, 2, 3], baz="Hello, world", qux=9000), bohica=(bar=[4, 5, 6], baz="Goodnight, moon", qux=2000))
  37.588 ns (4 allocations: 160 bytes)
(foo = (bar = [1, 2, 3], baz = "Hello, world", qux = 9000), bohica = (bar = [4, 5, 6], baz = "Goodnight, moon", qux = 2000))
julia> @btime b.foo.bar
  83.505 ns (2 allocations: 64 bytes)

```

For what I’m working on, I do not need the data structures to be mutable, so named tuples would work in that regard, but I will be spending a lot more runtime accessing the data than constructing it, so the 2x factor scared me off a bit. It may be that this is premature optimization, or it may be that I’m doing something wrong with the benchmark (for example, I don’t understand why named tuple access should be causing 2 allocations)…

I am starting to think more seriously about a macro based approach, though. It’s funny…Clojure, with its Lisp heritage, has one of the most powerful macro systems (after Scheme and Racket) and yet it’s very rarely used (and actively recommended _against_ by much of the community).

---

<div class="post-metadata">

### Author: ![tecosaur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tecosaur/32/23206_2.png) [@tecosaur](https://discourse.julialang.org/u/tecosaur)
#### Post date: [February 21, 2025, 3:00am UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/8 "2025-02-21T03:00:24Z")

</div>

> [@jballanc](#):
>
> Constructing the tuples is _way_ faster than constructing the `Dict`s, but access to nested members is 2x slower:

Careful about how you benchmark this, the results very much depend on whether the particular structure is known at compile time, see this example:

```julia-repl
julia> using Chairmarks

julia> ld = Dict(:foo => Dict(:bar => [1, 2, 3],
                         :baz => "Hello, world",
                         :qux => 9000),
            :bohica => Dict(:bar => [4, 5, 6],
                            :baz => "Goodnight, moon",
                            :qux => 2000))
Dict{Symbol, Dict{Symbol, Any}} with 2 entries:
  :bohica => Dict(:baz=>"Goodnight, moon", :bar=>[4, 5, 6], :qux=>2000)
  :foo => Dict(:baz=>"Hello, world", :bar=>[1, 2, 3], :qux=>9000)

julia> lt = (; foo = (; bar=[1,2,3], baz="Hello, world", qux=9000),
          bohica = (; bar=[4,5,6], baz="Goodnight, moon", qux=2000))
(foo = (bar = [1, 2, 3], baz = "Hello, world", qux = 9000), bohica = (bar = [4, 5, 6], baz = "Goodnight, moon", qux = 2000))

julia> @b ld[:foo][:bar]
28.184 ns

julia> @b $ld[:foo][:bar]
9.576 ns

julia> @b lt.foo.bar
75.581 ns (2 allocs: 64 bytes)

julia> @b $lt.foo.bar
2.359 ns

```

Using `$` interpolates the `ld`/`lt` and means the compiler has more type information, which is the situation if you were to use `lt.foo.bar` in a code, or pass `lt` into a function.

---

<div class="post-metadata">

### Author: ![oschulz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oschulz/32/2998_2.png) [@oschulz](https://discourse.julialang.org/u/oschulz)
#### Post date: [February 21, 2025, 8:48am UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/9 "2025-02-21T08:48:49Z")

</div>

I’ll add NamedTuple-syntax construction to PropDicts - anyone want to chime in, please post at

> <https://github.com/oschulz/PropDicts.jl/issues/5#issuecomment-2672807609>
>
> Construction could be more concise and more like NamedTuples or DataFrames.
> 
> \`…\`\`julia
> x = PropDict(a = PropDict(b = 7, c = 5, d = 2), e = "foo")
> \`\`\`

---

<div class="post-metadata">

### Author: ![jballanc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jballanc/32/328_2.png) [@jballanc](https://discourse.julialang.org/u/jballanc)
#### Post date: [February 22, 2025, 4:55am UTC](https://discourse.julialang.org/t/whats-your-preference-for-creating-literal-data-structures/126128/10 "2025-02-22T04:55:20Z")

</div>

Well, shoot…I thought we were getting more type info at the REPL top-level so I didn’t have to worry about such side-effects when benchmarking. Guess not!

I’m having serious second-thoughts about dismissing Named Tuples so quickly, especially since I realized that they can be spread out as args to a method accepting named parameters, saving a step in the code to pull out the relevant pieces of the data structure. e.g.:

```julia
julia> function findfoo(; foo=nothing, others...)
         if foo != nothing
           println("Found foo! It's: $(foo)")
         else
           for (_, v) ∈ others
             findfoo(;v...)
           end
         end
       end
findfoo (generic function with 1 method)

julia> a=(;b=(;c=(;d=(;e=(;foo="Hello, world!")))))
(b = (c = (d = (e = (foo = "Hello, world!",),),),),)

julia> findfoo(;a...)
Found foo! It's: Hello, world!

```

I just had the greatest idea for a visitor pattern implementation!
