# Function calling @SVector with locally defined range returns UndefVarError

**URL:** https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946
**Category:** General Usage
**Tags:** question
**Created:** [April 10, 2025, 7:29pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946 "2025-04-10T19:29:15Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![Giorgos\_Vretinaris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giorgos_vretinaris/32/214206_2.png) [@Giorgos\_Vretinaris](https://discourse.julialang.org/u/Giorgos_Vretinaris)
#### Post date: [April 10, 2025, 7:29pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/1 "2025-04-10T19:29:15Z")

</div>

Hello, I am trying to define this small function

```julia
f(n) = @SVector [i for i in 1:n]

```

and I get the following error:

```julia
ERROR: LoadError: UndefVarError: `n` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] top-level scope
   @ none:1
 [2] eval
   @ ./boot.jl:430 [inlined]
 [3] static_vector_gen(::Type{SVector}, ex::Any, mod::Module)
   @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/SVector.jl:62
 [4] var"@SVector"( __source__ ::LineNumberNode, __module__ ::Module, ex::Any)
   @ StaticArrays ~/.julia/packages/StaticArrays/LSPcF/src/SVector.jl:152
in expression starting at REPL[2]:1

```

Can I only use list comprehension with global scope ranges using `StaticArrays.jl`?

---

<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: [April 10, 2025, 7:59pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/2 "2025-04-10T19:59:59Z")

</div>

The macros need to get comprehension variables from the global scope. It’s for [inferring the size parameter](https://github.com/JuliaArrays/StaticArrays.jl/blob/19ecce783cb4bd44f80d3fe27faca84fefd5765e/src/SArray.jl#L217) at parse-time for better performance more easily. It’s documented so it’s stuck that way. Use the non-macro constructors and set the size yourself, preferably statically but not possible in this case.

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [April 10, 2025, 8:01pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/3 "2025-04-10T20:01:20Z")

</div>

Since `@SVector` is a macro and macros are evaluated at parse time (and `n` is not available at parse time), you get this error. It would only work if you used a parse-time constant range like `1:3`.

But there are numerous alternatives available, including

```julia-repl
julia> @SVector [i for i in 1:3] # works with the literal 3, but not a variable
3-element SVector{3, Int64} with indices SOneTo(3):
 1
 2
 3

julia> SVector{3}(i for i in 1:3)
3-element SVector{3, Int64} with indices SOneTo(3):
 1
 2
 3

julia> SVector{3}(1:3)
3-element SVector{3, Int64} with indices SOneTo(3):
 1
 2
 3

```

Note that the function `f(n) = SVector{n}(1:n)` is not type stable because the length `n` is a run time value (not compile time constant). So you are likely to see very poor performance if you use this function. You want the length to be brought into the type domain so that it is a compile-time constant. You might want to re-think exactly how you’re using this.

---

<div class="post-metadata">

### Author: ![Giorgos\_Vretinaris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giorgos_vretinaris/32/214206_2.png) [@Giorgos\_Vretinaris](https://discourse.julialang.org/u/Giorgos_Vretinaris)
#### Post date: [April 11, 2025, 6:44am UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/4 "2025-04-11T06:44:14Z")

</div>

Thank you both for your replies. But just to make sure, the length `n` being a run time value would make it non type stable for any type of stack allocated object, right? Because initially I was trying to just use ntuples but wasn’t able to get rid of `Any`’s and other red instances in `@code_warntype`. So, I guess I can’t avoid heap allocations for run time values.

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [April 11, 2025, 2:15pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/5 "2025-04-11T14:15:11Z")

</div>

`SVectors` are no different from `NTuple` in this regard: yes, the length and type information needs to be told to or inferable by the compiler to avoid boxing the value (sometimes not a big deal and other times costing considerable performance).

Often, I find that the size information is lying around and I just need to get it from an appropriate place. Like if I’m simulating the path of an object, I know whether it’s in 1D, 2D, or 3D space based on the length of its state (also an `SVector`).

There are ways to resolve this. Sometimes you can use a [function barrier](https://docs.julialang.org/en/v1.13-dev/manual/performance-tips/#kernel-functions) to mitigate a type instability. Another option (although sometimes tricky to do correctly) is to [pass the dimension in the type domain](https://docs.julialang.org/en/v1.13-dev/manual/performance-tips/#man-performance-value-type). If you read those resources and still struggle with how to do them, post a small example and people can help you work it out.

---

<div class="post-metadata">

### Author: ![Giorgos\_Vretinaris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giorgos_vretinaris/32/214206_2.png) [@Giorgos\_Vretinaris](https://discourse.julialang.org/u/Giorgos_Vretinaris)
#### Post date: [April 11, 2025, 2:58pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/6 "2025-04-11T14:58:13Z")

</div>

Thank you so much!

---

<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: [April 11, 2025, 4:58pm UTC](https://discourse.julialang.org/t/function-calling-svector-with-locally-defined-range-returns-undefvarerror/127946/7 "2025-04-11T16:58:12Z")

</div>

> [@Giorgos\_Vretinaris](#):
>
> the length `n` being a run time value would make it non type stable for any type of stack allocated object, right?

We don’t have direct control over what is stack-allocated or not. I suppose high-level languages don’t usually directly control where memory goes, but some languages have semantics that give you so much indirect manual control you practically know exactly where memory goes. Julia has some indirect control too, but memory allocation in a garbage collected language gives more decisions to the compiler.

The size of the `SVector` coming from the value of an argument usually means it’s only known during the method’s runtime, which we can benchmark to see some heap allocations:

```julia
julia> f(n) = SVector{n, typeof(n)}(1:n)
f (generic function with 1 method)

julia> @btime f($3);
  1.600 μs (11 allocations: 512 bytes)

```

However, the call itself could be taking a compile-time value, including constants, so if the method is small enough to be inlined (or you suggest it with `@inline`), some of its work can be done at the caller method’s compile-time. That can elide heap allocations, especially for immutables, which we can benchmark:

```julia
julia> g(::Val{n}) where n = f(n) # input static parameter
g (generic function with 1 method)

julia> @btime g($(Val(3)));
  1.100 ns (0 allocations: 0 bytes)

julia> g() = f(3) # input constant
g (generic function with 2 methods)

julia> @btime g();
  1.100 ns (0 allocations: 0 bytes)

```

Optimizing some work to compile-time doesn’t mean the called method as a whole runs at compile-time; for example, if you have a print statement, it’ll still execute every call.
