# Difference between vector of functions typed explicitly or via list comprehension

**URL:** https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671
**Category:** General Usage
**Tags:** question, type, function, closure
**Created:** [May 3, 2025, 6:00pm UTC](https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671 "2025-05-03T18:00:27Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![hawahe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hawahe/32/216495_2.png) [@hawahe](https://discourse.julialang.org/u/hawahe)
#### Post date: [May 3, 2025, 6:00pm UTC](https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671/1 "2025-05-03T18:00:27Z")

</div>

Are the following two ways to make a vector of functions equivalent? The latter gives both anonymous functions the name `#1` when I expected to see `#1` and `#2`.

```julia
julia> Function[(x) -> x^2, (x) -> x^3]
2-element Vector{Function}:
 #1 (generic function with 1 method)
 #2 (generic function with 1 method)

```

```julia
julia> Function[(x) -> x^i for i = 2:3]
2-element Vector{Function}:
 #1 (generic function with 1 method)
 #1 (generic function with 1 method)

```

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [May 3, 2025, 7:00pm UTC](https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671/2 "2025-05-03T19:00:00Z")

</div>

In the REPL:

```julia-repl
julia> hetero = Function[(x) -> x^2, (x) -> x^3]
2-element Vector{Function}:
 #3 (generic function with 1 method)
 #5 (generic function with 1 method)
 
julia> homo = Function[(x) -> x^i for i = 2:3]
2-element Vector{Function}:
 #8 (generic function with 1 method)
 #8 (generic function with 1 method)
 
julia> allequal(typeof, hetero)
false
 
julia> allequal(typeof, homo)
true

```

From the above we see that your first example vector is a heterogeneous collection, while your second example vector is a homogeneous collection (which just means that all elements have the same type).

What gives? The “vector of functions” part of the question is really just a red herring, this is really about closures and the ways of how types (in the _type system_ sense) of functions can work in Julia.

A function that’s not a closure, that is, a function that doesn’t capture anything, is a value of singleton type. In other words, it’s the only instance of its type. Example:

```julia-repl
julia> function f end
f (generic function with 0 methods)
 
julia> typeof(f)
typeof(f) (singleton type of function f, subtype of Function)
 
julia> typeof(f).instance # this is an implementation detail, the point is to show that there's just one instance to choose from
f (generic function with 0 methods)

```

At this point it might be worth it to say two tangentially relevant details:

- A function may have any number of methods, notice the above function `f` doesn’t have any, because adding a method was not relevant for the example. Adding methods to `f` from the above example will not (and can not) change the type of `f`.
- “Functions” are not very special in Julia. Any type can be made callable by adding methods to it. A special case, for example, are type constructors: a “constructor” is really just a type object with methods attached: [Constructors · The Julia Language](https://docs.julialang.org/en/v1/manual/constructors/#Constructors-are-just-callable-objects)

However, the same syntax is used for all of these Julia features:

- Declare a new function of singleton type: `function f end`
- Declare a new method of a function of a singleton type (with the function possibly also being new): `function f() 7 end` or `() -> 7`
- Declare a new method of a closure function, that is, a method that captures some variables from its enclosing scope. For example:

```julia-repl
julia> function f(x)
           function g()
               x + 1
           end
       end
f (generic function with 1 method)

julia> f(1)
(::var"#g#f##0"{Int64}) (generic function with 1 method)

julia> typeof(ans)
var"#g#f##0"{Int64}

julia> f(1) isa typeof(f(2))
true

julia> typeof(f(1)) == typeof(f(2))
true

```

In this case, although the type of the function may be parameterized by the type of the captured value (`Int`), the type of the function does _not_ depend on the captured value itself (`1` vs `2`).

To sum things up, in your first example you create two function types, while in the other there’s just one type, and it’s a closure.

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [May 3, 2025, 7:02pm UTC](https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671/3 "2025-05-03T19:02:54Z")

</div>

Usually you’d want to avoid heterogeneous collections, because using them is prone to performance pitfalls. See the **Performance tips** page in Julia’s Manual for more info:

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

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [May 3, 2025, 7:12pm UTC](https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671/4 "2025-05-03T19:12:59Z")

</div>

> [@hawahe](#):
>
> ```julia
> Function[(x) -> x^i for i = 2:3]
> 
> ```

Another thing to keep in mind, is that you probably want to omit the `Function` element type when constructing the `Vector`, because that results in a `Vector{Function}`, which, as the Performance tips will tell you, may result in bad performance, as `Function` is not a concrete type. Just doing `[(x) -> x^i for i = 2:3]`, without the `Function` prefix, solves this.

Although, to really say what’s the optimal choice, it’d would be necessary to know the entire picture regarding your program design. Avoiding type instability is a good rule of thumb in any case.

---

<div class="post-metadata">

### Author: ![sgaure](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sgaure/32/14779_2.png) [@sgaure](https://discourse.julialang.org/u/sgaure)
#### Post date: [May 4, 2025, 10:16am UTC](https://discourse.julialang.org/t/difference-between-vector-of-functions-typed-explicitly-or-via-list-comprehension/128671/5 "2025-05-04T10:16:51Z")

</div>

Just to add some more detail. A closure is not very different from a callable struct. Indeed, currently it’s implemented as one. We can mimic it, and see that one of the few differences (the only one?) between an anonymous closure and a callable struct is its name.

```julia
julia> struct FunStruct <: Function
           i::Int
       end

julia> function (f::FunStruct)(x)
           x ^ f.i
       end

julia> v = [FunStruct(i) for i in 2:3]
2-element Vector{FunStruct}:
 (::FunStruct) (generic function with 1 method)
 (::FunStruct) (generic function with 1 method)

julia> w = [x -> x^i for i in 2:3]
2-element Vector{var"#6#8"{Int64}}:
 #6 (generic function with 1 method)
 #6 (generic function with 1 method)

julia> dump(v)
Array{FunStruct}((2,))
  1: FunStruct(2) (function of type FunStruct)
    i: Int64 2
  2: FunStruct(3) (function of type FunStruct)
    i: Int64 3

julia> dump(w)
Array{var"#6#8"{Int64}}((2,))
  1: #6 (function of type var"#6#8"{Int64})
    i: Int64 2
  2: #6 (function of type var"#6#8"{Int64})
    i: Int64 3

julia> dump(var"#6#8"{Int64})
var"#6#8"{Int64} <: Function
  i::Int64

julia> dump(FunStruct)
FunStruct <: Function
  i::Int64

```
