# @pure macro

**URL:** <https://discourse.julialang.org/t/pure-macro/3871>\
**Category:** General Usage\
**Created:** [May 23, 2017, 4:51am UTC](https://discourse.julialang.org/t/pure-macro/3871 "2017-05-23T04:51:20Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![mohamed82008](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mohamed82008/32/18171_2.png) [@mohamed82008](https://discourse.julialang.org/u/mohamed82008)\
**Post date:** [May 23, 2017, 4:51am UTC](https://discourse.julialang.org/t/pure-macro/3871/1 "2017-05-23T04:51:20Z")

</div>

What does @pure do to a function? Is it faster in some cases?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [May 23, 2017, 5:28am UTC](https://discourse.julialang.org/t/pure-macro/3871/2 "2017-05-23T05:28:53Z")

</div>

I got rid of the “First Steps” tag. This is not something that should be discussed as a first step into Julia. This is very much digging deeper.

An `@pure` function is a function whose output is completely determined by its input. This means very many things. For one, no globals involved. Secondly, no pointers, since they are different in each session (this means no arrays). So something is `@pure` essentially if it’s a function of symbols, numbers, and booleans (other immutables, bitstypes, etc. which avoid pointers) that spits out some type which is (or only contains) these things.

It can make things faster. More correctly, it can help with inference. For example:

```julia
immutable Discrete{apply_map,scale_by_time} end
Discrete(;apply_map=false,scale_by_time=false) = Discrete{apply_map,scale_by_time}()

```

in this case, since the booleans are runtime variables, this will not actually be able to infer the output type without `@pure` because the output type depends on the type parameters, and the type parameters depend on the runtime values of the variables `apply_map` and `scale_by_time`.

```julia
julia> @code_warntype Discrete()
Variables:
  #self#::Type{Discrete}

Body:
  begin
      return ((Core.apply_type)(Main.Discrete,false,false)::Type{_<:Discrete})()::Discrete{apply_map,scale_by_time}
  end::Discrete{apply_map,scale_by_time}

```

However, with `@pure` the compiler does something like compile a separate version for each of the input types, and it’s then able to properly infer the output type.

```julia
immutable Discrete{apply_map,scale_by_time} end
Base.@pure Discrete(;apply_map=false,scale_by_time=false) = Discrete{apply_map,scale_by_time}()

julia> @code_warntype Discrete()
Variables:
  #self#::Type{Discrete}

Body:
  begin
      return $(QuoteNode(Discrete{false,false}()))
  end::Discrete{false,false}

```

So in some very specific case, `Base.@pure` will help inference.

---

<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:** [May 23, 2017, 2:37pm UTC](https://discourse.julialang.org/t/pure-macro/3871/3 "2017-05-23T14:37:43Z")

</div>

Just as a counterbalance, improper `@pure` annotations can introduce bugs. The optimizations it enables rely on an _extremely_ strict definition of pure. It really should be named something like `@hyperpure`. Some of the restrictions include:

- It must always return exactly (`===`) the same result for a given input. Watch out for mutable types. I think constant globals are okay, though.
- The function it’s used on cannot be further extended by other methods after it gets called.
- It cannot recurse.
- It’s undocumented and not exported (for good reason), but this means the complete list of preconditions is really only in a few people’s heads.

---

<div class="post-metadata">

**Author:** ![ExpandingMan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/expandingman/32/866_2.png) [@ExpandingMan](https://discourse.julialang.org/u/ExpandingMan)\
**Post date:** [May 23, 2017, 3:37pm UTC](https://discourse.julialang.org/t/pure-macro/3871/4 "2017-05-23T15:37:37Z")

</div>

This really should appear in the documentation (along with the appropriate warnings about use). I have seen it around, and never known what the hell it did.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [May 30, 2017, 6:40pm UTC](https://discourse.julialang.org/t/pure-macro/3871/5 "2017-05-30T18:40:38Z")

</div>

@mbauman about mutable types: it’s okay to use mutable types in the intermediate computation if the output is immutable, right? The case I am thinking of is building an `SVector` for the result, but using a `Vector` to internally build that. Since

```julia
using StaticArrays
a = @SArray [1,2,3]
b = @SArray [1,2,3]
a === b # true

```

it seems like it would work, but I just wanted to double check. If so, that makes it much easier to build type-inferrable `SArray`s in functions.

If that’s the case, then I believe a function like this should be `Base.@pure`?

[https://github.com/shivin9/PDEOperators.jl/blob/master/src/fornberg.jl#L34](https://github.com/shivin9/PDEOperators.jl/blob/master/src/fornberg.jl#L34)

---

<div class="post-metadata">

**Author:** ![iamed2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iamed2/32/215082_2.png) [@iamed2](https://discourse.julialang.org/u/iamed2)\
**Post date:** [August 11, 2017, 10:15pm UTC](https://discourse.julialang.org/t/pure-macro/3871/6 "2017-08-11T22:15:33Z")

</div>

> [@mbauman](#):
>
> The function it’s used on cannot be further extended by other methods after it gets called.

I’m on a quest to understand `@pure`. I saw this, but then looking through `AxisArrays` I saw:

```julia
@pure samesym{n1,n2}(::Type{Axis{n1}}, ::Type{Axis{n2}}) = Val{n1==n2}()
samesym{n1,n2,T1,T2}(::Type{Axis{n1,T1}}, ::Type{Axis{n2,T2}}) = samesym(Axis{n1},Axis{n2})
samesym{n1,n2}(::Type{Axis{n1}}, ::Axis{n2}) = samesym(Axis{n1}, Axis{n2})
samesym{n1,n2}(::Axis{n1}, ::Type{Axis{n2}}) = samesym(Axis{n1}, Axis{n2})
samesym{n1,n2}(::Axis{n1}, ::Axis{n2}) = samesym(Axis{n1}, Axis{n2})

```

Here the function is extended, but none of the following methods conflict with the `@pure` one. Is it more correct to say that the method it’s used on cannot be overloaded?

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [August 12, 2017, 3:46pm UTC](https://discourse.julialang.org/t/pure-macro/3871/7 "2017-08-12T15:46:04Z")

</div>

I think the key thing here is

> _after it gets called_

---

<div class="post-metadata">

**Author:** ![Elrod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elrod/32/22461_2.png) [@Elrod](https://discourse.julialang.org/u/Elrod)\
**Post date:** [August 13, 2017, 7:36am UTC](https://discourse.julialang.org/t/pure-macro/3871/8 "2017-08-13T07:36:03Z")

</div>

> [@ChrisRackauckas](#):
>
> So something is @pure essentially if it’s a function of symbols, numbers, and booleans (other immutables, bitstypes, etc. which avoid pointers) that spits out some type which is (or only contains) these things.
> 
> It can make things faster. More correctly, it can help with inference. For example:

Okay – seeing this, I wanted to create a type-stable function for PCA using SizedArrays.  
But it hasn’t worked in a simple test case so far:

```julia
julia> using StaticArrays

julia> p = 30; n = 40;

julia> S = randn(n, p) |> x -> x' * x;

julia> eigS = eigfact(S);

julia> λs = SVector{p}(cumsum(eigS.values));

julia> λs /= λs[end];

julia> E = SizedArray{Tuple{p,p}}(eigS.vectors);

julia> Base.@pure deduce_rank(x::SVector{p,<:Real}, ::Type{Val{g}}) where {p,g} = Val{p-searchsortedlast(x, 1-g)}
deduce_rank (generic function with 1 method)

julia> function f(x::SizedArray{Tuple{p,p}}, ::Type{Val{q}}) where {p,q}
           SizedArray{Tuple{p,q}}(x[:,p-q+1:end])
       end
f (generic function with 1 method)

julia> function h(X::SizedArray, x::SVector,::Type{Val{g}}) where g
           v = deduce_rank(x, Val{g})
           f(X, v)
       end
h (generic function with 1 method)

julia> h(E, λs, Val{0.9});

julia> typeof(ans)
StaticArrays.SizedArray{Tuple{30,18},Float64,2,2}

julia> @code_warntype h(E, λs, Val{0.9})
Variables:
  #self#::#h
  X::StaticArrays.SizedArray{Tuple{30,30},Float64,2,2}
  x::SVector{30,Float64}
  #unused#::Any
  v::Type{Val{_}} where _

Body:
  begin 
      $(Expr(:inbounds, false))
      # meta: location REPL[8] deduce_rank 1
      SSAValue(0) = $(Expr(:invoke, MethodInstance for searchsortedlast(::SVector{30,Float64}, ::Float64, ::Base.Order.ForwardOrdering), :(Base.Sort.searchsortedlast), :(x), :((Base.sub_float)((Base.sitofp)(Float64, 1)::Float64, 0.9)::Float64), :(Base.Sort.Forward)))
      # meta: pop location
      $(Expr(:inbounds, :pop))
      v::Type{Val{_}} where _ = (Core.apply_type)(Main.Val, (Base.sub_int)(30, SSAValue(0))::Int64)::Type{Val{_}} where _ # line 3:
      return (Main.f)(X::StaticArrays.SizedArray{Tuple{30,30},Float64,2,2}, v::Type{Val{_}} where _)::Any
  end::Any

```

Inference on deduce rank failed.  
Any suggestions?

I have a function that does something like this before doing \>1,000 matrix operations with the result.  
The cost of a dynamic dispatch is small in comparison to the benefit of using a sized array.  
But, would be great to find out a way to dodge the dynamic dispatch too.  
StaticArrays may not be sufficiently pure?

EDIT: You (and dextorious) showed me how to do those matrix operations much more rapidly with BLAS for anything but smallish dimensions (for which there’s no need for LDR).  
So I don’t need this, but still interesting.

---

<div class="post-metadata">

**Author:** ![jzr](https://avatars.discourse-cdn.com/v4/letter/j/eb9ed0/32.png) [@jzr](https://discourse.julialang.org/u/jzr)\
**Post date:** [June 4, 2021, 10:49pm UTC](https://discourse.julialang.org/t/pure-macro/3871/9 "2021-06-04T22:49:01Z")

</div>

> [@mbauman](#):
>
> - The function it’s used on cannot be further extended by other methods after it gets called.

After defining `@pure f(::T)` and calling it, can I define `f(::R)` if `R` isn’t a subtype of `T`?

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [June 5, 2021, 2:39am UTC](https://discourse.julialang.org/t/pure-macro/3871/10 "2021-06-05T02:39:22Z")

</div>

please see [All things considered, `@pure` is not for use outside of Base.](https://github.com/JuliaLang/julia/pull/39954#issuecomment-812876814)
