# Rationale for the \`@pure\`ity of \`Rational{T}(x)\`

**URL:** <https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872>\
**Category:** General Usage\
**Created:** [December 20, 2018, 11:23pm UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872 "2018-12-20T23:23:26Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 20, 2018, 11:23pm UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/1 "2018-12-20T23:23:26Z")

</div>

Looking at some example usages of `@pure` in `Base`, I cannot understand why `Rational{T}(x::AbstractIrrational)` is considered pure.

> <https://github.com/JuliaLang/julia/blob/851ae465c45c9db88f0a854646f28eb0589883e5/base/irrationals.jl#L32-L45>

`setprecision(BigFloat, _)` mutates a global state and also it may throw. I thought `Base.@pure` docstring prohibits any global mutable states. Is it OK to mutate a global state if the function rollbacks the change?

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [December 21, 2018, 2:16am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/2 "2018-12-21T02:16:53Z")

</div>

Looks like the precision value is not a regular global variable,

```nohighlight
precision(::Type{BigFloat}) = DEFAULT_PRECISION[] # precision of the type BigFloat itself

```

and have a look at this

```nohighlight
julia> Base.MPFR.DEFAULT_PRECISION
Base.RefValue{Int64}(256)

```

So there is definitely something different about it, although `Base.RefValue` does not have any doc string.

> <https://github.com/JuliaLang/julia/blob/master/base/refvalue.jl>

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 21, 2018, 2:26am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/3 "2018-12-21T02:26:32Z")

</div>

The docstring [Essentials · The Julia Language](https://docs.julialang.org/en/v1.2-dev/base/base/#Base.@pure) says

> `@pure` function cannot use any _global mutable state_, including generic functions

So it applies to any state, not just global _vairable_. That’s why it extends to a generic function (the mutable state there is the method table). I’m almost sure that it includes a `RefValue` (but it would be very interesting if not).

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [December 21, 2018, 2:27am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/4 "2018-12-21T02:27:53Z")

</div>

> [@tkf](#):
>
> I’m almost sure that it includes a `RefValue` (but it would be very interesting if not).

Looks like it is subtype of `Ref`

```Julia
help?> Base.Ref
  Ref{T}

  An object that safely references data of type T. This type
  is guaranteed to point to valid, Julia-allocated memory of
  the correct type. The underlying data is protected from
  freeing by the garbage collector as long as the Ref itself
  is referenced.

  In Julia, Ref objects are dereferenced (loaded or stored)
  with [].

  Creation of a Ref to a value x of type T is usually written
  Ref(x). Additionally, for creating interior pointers to
  containers (such as Array or Ptr), it can be written Ref(a,
  i) for creating a reference to the i-th element of a.

  When passed as a ccall argument (either as a Ptr or Ref
  type), a Ref object will be converted to a native pointer
  to the data it references.

  There is no invalid (NULL) Ref in Julia, but a C_NULL
  instance of Ptr can be passed to a ccall Ref argument.

```

---

<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:** [December 21, 2018, 2:30am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/5 "2018-12-21T02:30:33Z")

</div>

It does extend to `Ref` and `RefValue` –  
If one may alter any aspect of or connection with `φ` then `φ` cannot participate `@pure`ly.  
Purity is not restricted to Type info.

[the symbol \varphi above stands for whatever happens to be relevant to the discussion]

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 21, 2018, 2:34am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/6 "2018-12-21T02:34:41Z")

</div>

Can you look at my original question? Is it OK to be a `@pure` function if the mutation in the global state is rolled back? Maybe the real version of the spec is “`@pure` function cannot use any global mutable state in an _observable manner_” or something like that?

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 21, 2018, 2:38am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/7 "2018-12-21T02:38:38Z")

</div>

> [@JeffreySarnoff](#):
>
> If one may alter any aspect of or connection with `φ` then `φ` cannot participate `@pure` ly.

Actually, maybe you explained in this sentence. But it’s hard to understand it for me. (I can guess `φ` is the phi in LLVM IR but I’m not super familiar with it.)

---

<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:** [December 21, 2018, 2:40am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/8 "2018-12-21T02:40:15Z")

</div>

afaik, the basic reason for `@pure` is that _in very restricted_ and _tightly established_ circumstances … where there can be **no** influence outside of the context directly given as that which follows `@pure function` and concludes with the matching `end` … and there can be _no_ influence from within this context that extends outside of it … certain very strong presumptive compilation based runtime accelerators are available to be applied.

So any attempt to work-around purity or extend a pure function into a nonpure context is, by definition, inviting very hard to find bugs and errors in computational resolution. It does not mean they will occur; it does mean they might and there is no a priori way to know. (The manner of acceleration may change or new actions may be taken with the same pure context in a revision of Julia’s handling of `@pure` things.)

and this from Slack

> Simon Danisch [11:32]  
> I think this is mainly about what `@pure` promises the compiler… since lots of theoretical compiler optimizations are not implemented, in practise you can use `@pure` quite freely…  
> but it will be like when people did unsafe stuff in Julia 0.6, which worked fine because we didn’t have agressive optimizations…and then suddenly it started segfaulting on 1.0, when we got more aggressive gc elimination passes

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 21, 2018, 2:57am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/10 "2018-12-21T02:57:29Z")

</div>

Thanks, my question here is rather to understand why such strict restrictions you listed do not apply to the method `Rational{T}(x::AbstractIrrational)` exists in `Base`. (Rather than to discuss, as in [Extending methods of pure function](https://discourse.julialang.org/t/extending-methods-of-pure-function/18852), how to workaround such restriction, if possible at all).

If you think `Rational{T}(x::AbstractIrrational)` violates the assumptions of purity, I think we should make a PR.

The reason I can think of why `Rational{T}` is `@pure` is that:

- It rolls back the global state. Thus, executing the body of it or not does not change the semantics of the code following it.
- The second argument `p` in `setprecision(BigFloat, p)` never hits the branch in `setprecision` which `throw`s.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [December 21, 2018, 3:14am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/11 "2018-12-21T03:14:27Z")

</div>

It would seem that `Rational{Int}(π)` returns the same value as `rationalize` does, and if you change the precision globally, it does not affect the results of the calculation.

```Julia
julia> rationalize(Int,π,tol=0)
2646693125139304345//842468587426513207

julia> Rational{Int}(π)
2646693125139304345//842468587426513207

julia> setprecision(BigFloat,512)
512

julia> Rational{Int}(π)
2646693125139304345//842468587426513207

```

Also, I am trying to understand why the inequality here is ` > eps(bx)` but maybe I just don’t understand how this algorithm is supposed to work…

> [@tkf](#):
>
> `if abs(BigFloat(r) - bx) > eps(bx)`

Is it supposed to increase the precision at each iteration? This algorithm doesn’t quite make sense.

---

<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:** [December 21, 2018, 6:36am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/12 "2018-12-21T06:36:30Z")

</div>

> [@tkf](#):
>
> Thanks, my question here is rather to understand why such strict restrictions you listed do not apply to the method `Rational{T}(x::AbstractIrrational)` exists in `Base` . (Rather than to discuss, as in [Extending methods of pure function](https://discourse.julialang.org/t/extending-methods-of-pure-function/18852), how to workaround such restriction, if possible at all).
> 
> If you think `Rational{T}(x::AbstractIrrational)` violates the assumptions of purity, I think we should make a PR.

It will cause issues. Specifically, `@pure` allows the compiler to compile one version that basically just spits out a pre-computed answer based on the input types here. That makes it so it won’t ever re-check the global after the first time it’s compiled, which is what @chakravala’s example is then showing. That is a feature of `@pure`, and why it shouldn’t be used in this case. I think it was added assuming BigFloat had one precision, which was incorrect and a PR should correct it.

---

<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:** [December 21, 2018, 7:11am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/13 "2018-12-21T07:11:34Z")

</div>

Yeah, I think this is a bug.

[https://github.com/JuliaLang/julia/blame/6c4429b2b547b3a3ae3087a2245055d044ceca4d/base/irrationals.jl#L18-L21](https://github.com/JuliaLang/julia/blame/6c4429b2b547b3a3ae3087a2245055d044ceca4d/base/irrationals.jl#L18-L21)

got changed into

[https://github.com/JuliaLang/julia/blame/a02c7312d2e5ef0f047765e4df7bfd38c084573d/base/irrationals.jl#L18-L31](https://github.com/JuliaLang/julia/blame/a02c7312d2e5ef0f047765e4df7bfd38c084573d/base/irrationals.jl#L18-L31)

in [Fix conversion Irrational =\> Rational by simonbyrne · Pull Request #16527 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/16527), and it’s just been carried over ever since.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [December 21, 2018, 9:38am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/14 "2018-12-21T09:38:48Z")

</div>

> [@tkoolen](#):
>
> in [https://github.com/JuliaLang/julia/pull/16527](https://github.com/JuliaLang/julia/pull/16527), and it’s just been carried over ever since.

> [@](#):
>
> If `T` is a `AbstractFloat` or `Rational` type, then it will return the closest value to `x` representable by `T` .

> [@](#):
>
> The aim of the `@generated` is to get it to perform the calculation at compile time, as the result will always be constant. This is exactly the same as the usage in the function directly below.
> 
> Technically, an `@pure` annotation would be sufficient , once [#14324](https://github.com/JuliaLang/julia/issues/14324) is sorted out.

This might be correct after all, since the result depends on the representation by `T`, which is invariant under changes with respect to the `BigFloat` precision, i.e. constant.

Therefore, the `@pure` macro was used. So this is why this function doesn’t really depend on a global state, because the result shouldn’t really change anyway, because the representation in `T` won’t change.

So the result doesn’t really depend on the global state of `BigFloat` precision, also it seems that `rationalize` would also be sufficient for irrationals, and the rest of the algorithm is unnecessary?

EDIT: ahh now I see how it works: in case you changed the `BigFloat` precision before calling this function, this algorithm ensures that it uses the correct precision value and resets it after, but overall, it does not depend on the global state of the precision. The result does not and should not change.

So it seems the `@pure` macro was used here in a very special use case, where a global variable is used in the algorithm, which only needs to be used once at compile time and is otherwise irrelevant. The use of the global variable is only to ensure that the computation uses the standard precision at compilation, the state of the global variable does not and should not matter otherwise. Now it all makes sense.

I believe this would be a sufficient algorithm

```Julia
@pure function Rational{T}(x::AbstractIrrational) where T<:Integer
    o = precision(BigFloat)
    setprecision(BigFloat, 256)
    r = rationalize(T, x, tol=0)
    setprecision(BigFloat, o)
    return r
end

```

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 22, 2018, 12:38am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/15 "2018-12-22T00:38:58Z")

</div>

Thanks for detailed explanation. Probably I should have summarized that in the first post (I thought quoting the code was enough). To clarify, my question has been: operationally `Rational{T}(x::AbstractIrrational)` _looks_ like a pure function but does it really avoid restriction of `@pure`? Is it OK to mutate global states in `@pure` if it is not _observable_? If so, what is the definition of the _observability_?

For example, for `Rational{T}(x::AbstractIrrational)` to be safe when multithreading becomes a normal style in Julia, I think `DEFAULT_PRECISION` has to be guarded by a lock. An interesting question is that “does accruing a lock mutate the global state?” It certainly is “observable” in the sense that not accruing a lock can turn dead-locking program to a non-dead-locking program. But then maybe things are not “observable” when it does not alter “correct” programs?

> [@chakravala](#):
>
> I believe this would be a sufficient algorithm

But how do you know 256 is enough? Also, if you want to discuss this aspect of `Rational{T}`, it’s maybe a good idea to open a new topic (or a github issue).

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [December 22, 2018, 12:54am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/16 "2018-12-22T00:54:33Z")

</div>

> [@tkf](#):
>
> But how do you know 256 is enough? Also, if you want to discuss this aspect of `Rational{T}` , it’s maybe a good idea to open a new topic (or a github issue).

It would only be enough for native Julia types, such as `Int`, `UInt128`, etc. More generally, it would not be enough. I wasn’t planning on making a Pull Request for it, just analyzing the usage of `setprecision` and the `@pure` macro in this particular situation. Could someone else comment on this?

> [@tkf](#):
>
> To clarify, my question has been: operationally `Rational{T}(x::AbstractIrrational)` _looks_ like a pure function but does it really avoid restriction of `@pure` ? Is it OK to mutate global states in `@pure` if it is not _observable_ ? If so, what is the definition of the _observability_ ?

I can’t answer this, because I am not knowledgeable enough, but based on my findings, it looks like in this particular case it is valid.

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [December 22, 2018, 1:30am UTC](https://discourse.julialang.org/t/rationale-for-the-pure-ity-of-rational-t-x/18872/17 "2018-12-22T01:30:52Z")

</div>

As I said, I’ve already noticed what you found. I also explained why that’s not enough.
