# How does Any actually work?

**URL:** <https://discourse.julialang.org/t/how-does-any-actually-work/124135>\
**Category:** New to Julia\
**Tags:** question\
**Created:** [December 23, 2024, 7:13pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135 "2024-12-23T19:13:32Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 23, 2024, 7:13pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/1 "2024-12-23T19:13:32Z")

</div>

Earlier I posted an example function which has very poor runtime performance.

```julia
function demo(
    dict::Dict{Symbol, Vector},
    datetime_first::DateTime,
    datetime_last::DateTime,
)
    time = dict[:time]
    data = dict[:data]

    sum_value = 0.0f0
    sum_count = 0

    for ii in 1:length(time)
        if time[ii] >= datetime_first
            if time[ii] < datetime_last
                sum_value += data[ii]
                sum_count += 1
            end
        end
    end

    average_value = sum_value / sum_count

    return average_value
end

```

For an input of length 10^8 this should run in a few ms, instead it takes nearly a second.

The reason for the poor performance is all the variables in this function are inferred to have the type `Any`.

For me, this raised two questions.

- Firstly, what actually is `Any`? I understand that it is not a concrete type, so there is no memory representation for an `Any` type. So, if `sum_value` and `sum_count` are of type `Any`, what sense can be made of this? In reality they are of type `Float32` and `Int64` respectively. So why does the machine code produce inefficient code?
- Secondly, when the input data `dict` contains the following elements, what does the compiler actually do when it generates the code for `function demo`?

```julia
time::Vector{Float32} = ...
data::Vector{Float32} = ...
dict = Dict(:time => time, :data => data)

```

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [December 23, 2024, 9:38pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/2 "2024-12-23T21:38:27Z")

</div>

> [@world-peace](#):
>
> `dict = Dict(:time => Vector{DateTime}, :data => Vector{Float32})`

The variable `dict` has the type `Dict{Symbol, DataType}`, which would cause an error if passed as the first argument to your function `demo`. To construct an empty vector, use `DateTime[]` rather than `Vector{DateTime}`.

In any case, the compiler generates code for the argument type `dict::Dict{Symbol, Vector}` and won’t re-generate more performant code (i.e. specialization) depending on the actual `dict` argument you supplied. The compiler skips specialization because `Dict{Symbol, Vector}` is actually a concrete type, but it’s still a performance liability because it defines a container with non-concrete type `Vector` for the values in the dictionary. The consequence is that the compiler cannot infer the type of the actual elements in the vector.

> [@world-peace](#):
>
> Firstly, what actually is `Any`?

An object of type `Any` is represented in memory as a label of the actual concrete type and a memory address which points to a variable-length chunk of memory (the length depends on the exact concrete type) that holds the data. Since the length is unpredictable, the memory is allocated in unpredictable non-contiguous locations, making performance bad.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 23, 2024, 9:51pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/3 "2024-12-23T21:51:10Z")

</div>

> [@greatpet](#):
>
> The variable `dict` has the type `Dict{Symbol, DataType}`, which would cause an error if passed as the first argument to your function `demo`. To construct an empty vector, use `DateTime[]` rather than `Vector{DateTime}`.

I’ve slightly re-formatted that line of code to better express what I was intending.

It’s just a dictionary with `Symbol` as the key, and two `Vector`s as the values. The `Vector`s are different types; `Vector{Float32}` and `Vector{DateTime}`.

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [December 23, 2024, 9:54pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/4 "2024-12-23T21:54:18Z")

</div>

> [@world-peace](#):
>
> `dict = Dict(:time => time (Vector{DateTime}), :data => data (Vector{Float32}))`

Your re-worked code triggers a syntax error (just type it into the REPL to see). You could use this instead:

```julia
dict = Dict(:time => DateTime[], :data => Float32[])

```

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 23, 2024, 9:55pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/5 "2024-12-23T21:55:20Z")

</div>

> [@greatpet](#):
>
> An object of type `Any` is represented in memory as a label of the actual concrete type and a memory address which points to a variable-length chunk of memory (the length depends on the exact concrete type) that holds the data. Since the length is unpredictable, the memory is allocated in unpredictable non-contiguous locations, making performance bad.

This is what I don’t understand. If I pass in this object

```julia
time::Vector{DateTime}
data::Vector{Float32}
dict = Dict(:time=>time, :data=>data)

```

why does that result in poor performance.

Take for example the `Vector{Float32}`. Its in memory representation does not change when added to this `dict` object, or when that `dict` is passed to the function, or when the function de-structures the `dict` into a pair of `Vector`s.

So, each element (type `Any`) is not a pointer to a memory location containing `(sizeof(Float32), value::Float32)`. It’s just an array of `Float32`.

Hence my confusion - hope that makes sense?

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 23, 2024, 9:55pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/6 "2024-12-23T21:55:50Z")

</div>

> [@greatpet](#):
>
> Your re-worked code triggers a syntax error (just type it into the REPL to see). You could use this instead:

This is not intended to be code which compiles, please do not be distracted by that. It’s just pseudocode to show what the type is.

I have edited it to make it clearer.

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [December 23, 2024, 10:09pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/7 "2024-12-23T22:09:47Z")

</div>

> [@world-peace](#):
>
> If I pass in this object
> 
> ```julia
> time::Vector{DateTime}
> data::Vector{Float32}
> dict = Dict(:time=>time, :data=>data)
> 
> ```
> 
> why does that result in poor performance.

Try inspecting the type of `dict`:

```julia
julia> typeof(Dict(:time => DateTime[], :data => Float32[]))
Dict{Symbol, Vector}

```

You’re getting a `Dict` with a non-concrete `Vector` type for the values. This is because you mixed two vectors with different element types.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 23, 2024, 10:12pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/8 "2024-12-23T22:12:25Z")

</div>

Is it a really stupid comment if I say the following?

- If a function takes an argument of type `Vector` (which is abstract) then it will generate code for every possible concrete type of `Vector{T}` which could be passed to it?

It seems like such a function would be infinitely long because of the infinite possibilities in the argument types - but perhaps not?

For example, if the function body just added all the elements

```julia
function example(v::Vector)
    accum = 0.0f0
    for i in eachindex(v)
        value = v[i]
        accum += value
    end
end

```

then I suppose there are two (three) functions which need to exist

- a function to get an element by index from `v`
- a function to convert `value` to `Float32`
- (the add function for two `Float32` arguments - but this one isn’t as important since the types are concrete by this point)

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [December 23, 2024, 10:12pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/9 "2024-12-23T22:12:30Z")

</div>

`Any` matches any Julia type. You can think of it similar to what a void pointer might be in C. This pointer may then point to something that has a type tag. We can only figure out what the type of the pointed to object is by deferencing the pointer. Additionally, the type of the pointed to memory could change.

As a contrived example, I will use references below which are similar to pointers. I will then compile the functions to LLVM IR.

The example using `Int` compiles to a few simple LLVM instructions. In fact, it figured out that I’m just multiplying the value by 2, meaning we can just do a left shift.

The example using `Any` results in many more LLVM instructions. Here we have to carefully dereference the pointers, figure out the types, and then apply a generic add function, dynamically dispatching at runtime. As you can see this is considerably less efficient than the `Int` example. It becomes similar to what an untyped language like Python might do in its default execution model.

```julia-repl
julia> r_int = Ref{Int}(1)
Base.RefValue{Int64}(1)

julia> r_any = Ref{Any}(1)
Base.RefValue{Any}(1)

julia> f(r) = r[] + r[]
f (generic function with 3 methods)

julia> @code_llvm f(r_int)
; @ REPL[82]:1 within `f`
define i64 @julia_f_832({}* noundef nonnull align 8 dereferenceable(8) %0) #0 {
top:
; ┌ @ refvalue.jl:59 within `getindex`
; │┌ @ Base.jl:37 within `getproperty`
    %1 = bitcast {}* %0 to i64*
    %2 = load i64, i64* %1, align 8
; └└
; ┌ @ int.jl:87 within `+`
   %3 = shl i64 %2, 1
; └
  ret i64 %3
}

julia> @code_llvm f(r_any)
; @ REPL[82]:1 within `f`
define nonnull {}* @julia_f_834({}* noundef nonnull align 8 dereferenceable(8) %0) #0 {
top:
  %1 = alloca [2 x {}*], align 8
  %gcframe8 = alloca [3 x {}*], align 16
  %gcframe8.sub = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe8, i64 0, i64 0
  %2 = bitcast [3 x {}*]* %gcframe8 to i8*
  call void @llvm.memset.p0i8.i64(i8* align 16 %2, i8 0, i64 24, i1 true)
  %thread_ptr = call i8* asm "mrs $0, tpidr_el0", "=r"() #7
  %tls_ppgcstack = getelementptr i8, i8* %thread_ptr, i64 16
  %3 = bitcast i8* %tls_ppgcstack to {} ****
  %tls_pgcstack = load {} ***, {}**** %3, align 8
; ┌ @ refvalue.jl:59 within `getindex`
; │┌ @ Base.jl:37 within `getproperty`
    %4 = bitcast [3 x {}*]* %gcframe8 to i64*
    store i64 4, i64* %4, align 16
    %5 = load {} **, {}*** %tls_pgcstack, align 8
    %6 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe8, i64 0, i64 1
    %7 = bitcast {} **%6 to {}***
    store {} **%5, {}*** %7, align 8
    %8 = bitcast {} ***%tls_pgcstack to {}***
    store {} **%gcframe8.sub, {}*** %8, align 8
    %9 = bitcast {}* %0 to {}**
    %getfield = load atomic {}*, {}** %9 unordered, align 8
    %.not = icmp eq {}* %getfield, null
    br i1 %.not, label %fail, label %pass4

fail: ; preds = %top
    call void @ijl_throw({}* inttoptr (i64 530752553744 to {}*))
    unreachable

pass4: ; preds = %top
    %.sub = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 0
    %10 = getelementptr inbounds [3 x {}*], [3 x {}*]* %gcframe8, i64 0, i64 2
    store {}* %getfield, {}** %10, align 16
; └└
  store {}* %getfield, {}** %.sub, align 8
  %11 = getelementptr inbounds [2 x {}*], [2 x {}*]* %1, i64 0, i64 1
  store {}* %getfield, {}** %11, align 8
  %12 = call nonnull {}* @ijl_apply_generic({}* inttoptr (i64 530685332464 to {}*), {}** nonnull %.sub, i32 2)
  %13 = load {}*, {}** %6, align 8
  %14 = bitcast {} ***%tls_pgcstack to {}**
  store {}* %13, {}** %14, align 8
  ret {}* %12
}

```

To summarize, if the type is known we can compile to a succint set of instructions that we can determine at _compile_ time. `Any` represents a wildcard unknown type, forcing us to figure out what needs to be done at _run_ time.

---

<div class="post-metadata">

**Author:** ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)\
**Post date:** [December 23, 2024, 10:24pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/10 "2024-12-23T22:24:06Z")

</div>

> [@world-peace](#):
>
> If a function takes an argument of type `Vector` (which is abstract) then it will generate code for every possible concrete type of `Vector{T}` which could be passed to it?

Not necessarily, the following function will have good performance:

```julia
function example(v::Vector)
    accum = zero(eltype(v))
    for a in v
        accum += a
    end
    return accum
end

```

This is because the function argument `v` is annotated with an abstract type `Vector`, and the compiler will generate the code for the actual type of the argument passed to the function, e.g. `Vector{Float64}`. However, the following function will have poor performance:

```julia
function example(dict::Dict{String, Vector})
    v = dict["data"]
    accum = zero(eltype(v))
    for a in v
        accum += a
    end
    return accum
end

```

This is because the function argument `dict` is annotated with a concrete type `Dict{String, Vector}`, in which case the compiler will not further specialize for user-supplied arguments, and the generated code is not aware of the type of the elements in the vector. (In Julia’s type system, Dicts with non-concrete key/value types can still have concrete types themselves.)

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 23, 2024, 10:38pm UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/11 "2024-12-23T22:38:50Z")

</div>

You’re just giving me surface level details and telling me information I already know which is pointless, distracts from the thread, and gives the impression to other readers that the question has been answered because of the long series of posts which focus on irrelevant nit-picking pedanticities.

We just don’t speak the same language. I am sorry.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [December 24, 2024, 1:07am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/12 "2024-12-24T01:07:23Z")

</div>

> [@world-peace](#):
>
> If a function takes an argument of type `Vector` (which is abstract) then it will generate code for every possible concrete type of `Vector{T}` which could be passed to it

There are several levels of issues to unpack here. The exact example you gave lacks the nesting issue that was present with `Dict` making this a different situation.

Here `Vector` will match any of `Vector`, `Vector{Int}`, or `Vector{Any}`. Once matched for the purposes of method dispatch. Then, Julia will generate and specialize the code. In this case, Julia can infer the type and thus generate code Just-In-Time.

```julia
julia> f(v::Vector) = v[1] + v[1]
f (generic function with 1 method)

julia> f(Int[1,2])
2

julia> @code_llvm f(Int[1,2])
....
; ┌ @ int.jl:87 within `+`
   %3 = shl i64 %arrayref, 1
; └
  ret i64 %3
}

julia> @code_warntype f(Int[1,2])
MethodInstance for f(::Vector{Int64})
  from f(v::Vector) @ Main REPL[1]:1
Arguments
  #self#::Core.Const(f)
  v::Vector{Int64}
Body::Int64
1 ─ %1 = Base.getindex(v, 1)::Int64
│ %2 = Base.getindex(v, 1)::Int64
│ %3 = (%1 + %2)::Int64
└── return %3

julia> methods(f, Tuple{Vector{Int}}).ms[1].specializations
MethodInstance for f(::Vector{Int64})

```

The difference between this example and the original example with `Dict{Symbol, Vector}` issue is that the `Dict{Symbol, Vector}` has an explicitly abstract value type and thus cannot be specialialized. In contrast `Dict{Symbol, <: Vector}` and `Vector` can be specialialized by Julia just-in-time.

- `Dict{Symbol, Vector}` means a dictionary with key type `Symbol` and explicitly value type of an unparameterized `Vector`.
- `Dict{Symbol, <: Vector}` means a dixtionary with key type `Symbol` and any subtype of `Vector`. Subtypes of `Vector` include both an unparameterized `Vector` itself and parameterized types such as `Vector{Int}`.
- `Vector` by itself is equivalent to `Vector{ <: Any}`, meaning it will match any or no parameter.
- `Vector{Any}` without the subtyping operator is distinct from `Vector` or `Vector{ <: Any}`. Like `Dict{Symbol, Vector}` it explicitly says that the parameter is specifically `Any`, not a subtype of it.

> it will generate code for every possible concrete type of `Vector{T}` which could be passed to it

I hesitate to agree with this. I initially thought that you meant that Julia would generate code _eagerly_ for every single concrete type of `Vector{T}`. Julia will not _eagerly_ do so. With the current execution model, Julia will only generate this code _when_ a concrete type of `Vector{T}` would be passed to it. In other words, Julia is lazy about code generation.

One major caveat to the above statement is that there is a new experimental mode of Julia compilation where Julia will attempt to _eagerly_ determine all possible types when the `juliac` driver is used. This again is experimental and requires one to get into the nightly development builds. This mode is meant for static compilation where the set of “every possible concrete type of `Vector{T}` which could be passed to it” can be constrained under the assumption that no new code will be added. This assumption is not valid meant for the primary execution mode of Julia since Julia’s current execution model does allow new code to be added.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [December 24, 2024, 1:17am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/13 "2024-12-24T01:17:01Z")

</div>

Here’s a few additional resources:

1. [Julia Type System Explained](https://blog.glcs.io/julia-type-system#heading-abstract-types) (from JuliaCon 2024 sponsor Great Lakes Consulting) - "Just like Abstract types, if you leave off a supertype when you declare the primitive, the primitive will have “Any” as its direct supertype. "
2. [[2310.16866] A Type System for Julia](https://arxiv.org/abs/2310.16866) (A PhD Thesis by Benjamin Chung) - “The type Any is the root of the type hierarchy, or the greatest  
supertype (top).”
3. [https://dl.acm.org/doi/pdf/10.1145/3485527](https://dl.acm.org/doi/pdf/10.1145/3485527). " Type stability in Julia: avoiding performance pathologies in JIT compilation", [Julia Belyakova](https://julbinb.github.io/).

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [December 24, 2024, 2:28am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/14 "2024-12-24T02:28:56Z")

</div>

> [@world-peace](#):
>
> You’re just giving me surface level details and telling me information I already know which is pointless, distracts from the thread

It seems like people are quoting and adequately answering several of your questions; I myself had asked similar questions before and gotten similar answers. People can’t know what you know or don’t know, so it’s not their fault if they don’t happen to offer entirely new information. It’s your responsibility to clarify what pieces of Julia’s type inference and compilation implementation you’re missing.

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [December 24, 2024, 3:13am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/15 "2024-12-24T03:13:47Z")

</div>

Argument type annotations _do not affect performance_\*.

They just define the dispatch table and constrain the possible types that the method accepts. It just so happens that `::Dict{String, Vector}` will only accept a type unstable collection. And that’s the crux of the problem here.

> **[Performance Tips · The Julia Language](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container)**
>
> Documentation for The Julia Language.

\* except when they change specialization heuristics, but these are not that case

> [@world-peace](#):
>
> - If a function takes an argument of type `Vector` (which is abstract) then it will generate code for every possible concrete type of `Vector{T}` which could be passed to it?
> 
> It seems like such a function would be infinitely long because of the infinite possibilities in the argument types - but perhaps not?

This is correct — but Julia will only generate that code if and when it sees that particular argument type. This is precisely why the time to first X is a problem — if Julia hasn’t seen some type yet it’ll need to pause and compile the method.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 24, 2024, 8:56am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/16 "2024-12-24T08:56:11Z")

</div>

> [@Benny](#):
>
> It seems like people are quoting and adequately answering several of your questions; I myself had asked similar questions before and gotten similar answers. People can’t know what you know or don’t know, so it’s not their fault if they don’t happen to offer entirely new information.

I suppose I was hoping someone who works with the compiler could provide an in-depth explanation.

To say “type is not concrete so slow” is just repeating what the documentation pages say. It’s unhelpful because it does not offer any explanation as to what output the compiler produces.

Only by knowing that information can we understand _why_ it is slow.

The problem is, I am not an expert in LLVM. So I cannot read the LLVM output.

I should perhaps add the context to this that as well as Julia, I also have a deep understanding of how C++ behaves. I can read the machine code the compiler generates (although again it is not easy to do so, and it takes me a long time) so I would not suggest reading the machine code generated by the Julia compiler is a realistic starting point.

Since C++ is statically typed, you can’t accidentally tank your code performance by not fully specifying the types to the compiler.

So trying to understand what Julia does with types like `Dict{:Symbol, :Vector}` is my main focus here.

To put a different angle on it, understanding why Python performance is poor is relatively simple. All types are effectively `Any`. At runtime, when a function is called with some arguments, Python has to go to the location of each argument, figure out what (runtime) type it has, then follow a pointer to a VTable. The VTable tells Python what instructions to execute to perform the operations of the function.

It is the repeated querying of “what type are you” which contributes significantly to the performance hit. If a function is implemented in Python code rather than at the C level, the effect is compounded for each variable and operation inside the function.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 24, 2024, 9:07am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/17 "2024-12-24T09:07:42Z")

</div>

> [@mbauman](#):
>
> This is correct — but Julia will only generate that code if and when it sees that particular argument type.

See this doesn’t make sense to me. Consider the example argument type of

```julia
data::Vector{Float32}
time::Vector{DateTime}
dict(:data=>data, :time=>time)

```

Sorry to keep repeating myself. This has type

```julia
Dict{Symbol, Vector}

```

which happens to match the function argument type, which is the same.

When the compiler first sees this function call, it will generate machine code for the function with argument of type `Dict{Symbol, Vector}`.

This doesn’t make sense to me, because as was already pointed out, `Vector` is an abstract type, and therefore it can be infinitely many possible things.

- I guess a better way to phrase the question here is _what does the Julia compiler do to constrain_ `Dict{Symbol, Vector}` _further when it generates code for a function with this argument type?_

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 24, 2024, 9:08am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/18 "2024-12-24T09:08:41Z")

</div>

Thanks for your comments, this looks useful. Allow me some time to read through this.

---

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [December 24, 2024, 9:38am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/19 "2024-12-24T09:38:08Z")

</div>

This is a subtle point, but while `Vector` is not a concrete type, `Vector{Vector}` is (and same for `Dict{Symbol, Vector}`). A concrete type is something you can instantiate. It’s the type of some object. There’s no object whose `typeof(x)` is `Vector`, but `typeof(Vector[])` is `Vector{Vector}`. The compiler specializes on the concrete type of the arguments, and you have to think of what information it has. If `v` is known to be `Vector{Vector}`, then `v[3] = some_vector` will be efficient code, but `sin(v[3][1])` will be inefficient, because the compiler won’t know which method of `sin` to call (is `v[3][1]` an Int? A float?) so it has to look it up at runtime. Whereas if `v` is a `Vector{Vector{Int}}`, then it’ll know exactly which code to run to compute `sin(::Int)`, so it can be much faster.

---

<div class="post-metadata">

**Author:** ![world-peace](https://avatars.discourse-cdn.com/v4/letter/w/9f8e36/32.png) [@world-peace](https://discourse.julialang.org/u/world-peace)\
**Post date:** [December 24, 2024, 9:50am UTC](https://discourse.julialang.org/t/how-does-any-actually-work/124135/20 "2024-12-24T09:50:31Z")

</div>

> [@mkitti](#):
>
> The difference between this example and the original example with `Dict{Symbol, Vector}` issue is that the `Dict{Symbol, Vector}` has an explicitly abstract value type and thus cannot be specialialized.

Ok so what you have shown here is that the example with `Vector` does not produce any warnings with `@warn_type` and is therefore not representative of the original problem. I agree with that.

> [@mkitti](#):
>
> I hesitate to agree with this. I initially thought that you meant that Julia would generate code _eagerly_ for every single concrete type of `Vector{T}`. Julia will not _eagerly_ do so.

Yes, this is what I intended to say but as you pointed out, and I had already realized, this would be logically impossible because there are an infinite number of possible types.

[Next page](https://discourse.julialang.org/t/how-does-any-actually-work/124135.md?page=2)
