# Bypass inner constructor / call \`new\` outside inner constructor

**URL:** <https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142>\
**Category:** General Usage\
**Tags:** constructors\
**Created:** [March 26, 2024, 1:37pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142 "2024-03-26T13:37:56Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [March 26, 2024, 1:37pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/1 "2024-03-26T13:37:56Z")

</div>

I have defined a struct with an inner constructor that enforces some invariants,  
similar to the example in the [Documentation](https://docs.julialang.org/en/v1/manual/constructors/#man-inner-constructor-methods)

```julia
julia> struct OrderedPair
           x::Real
           y::Real
           OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
       end

```

and was wondering if there is a way to bypass the inner constructor and call `new` directly when I am absolutely sure that the invariants are satisfied. E.g. to define `copy` I can do the following

```julia
julia> Base.copy(op::OrderedPair) = OrderedPair(op.x, op.y)

```

but now the inner constructor is called again and `op.x > op.y` tested again, even though we can be sure this is the case. Is there way to do e.g.

```julia
julia> Base.copy(op::OrderedPair) = new(OrderedPair, op.x, op.y)

```

that directly constructs the struct without going through the inner constructor?

---

<div class="post-metadata">

**Author:** ![screw\_dog](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/screw_dog/32/48119_2.png) [@screw\_dog](https://discourse.julialang.org/u/screw_dog)\
**Post date:** [March 26, 2024, 1:53pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/2 "2024-03-26T13:53:25Z")

</div>

One way would be just to add another inner constructor.

```julia
struct OrderedPair
  x::Real
  y::Real
  OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
  OrderedPair(op::OrderedPair) = new(copy(op.x), copy(op.y))
end

```

However, this generally won’t do what you want because since this is immutable and most `Real` types are also immutable, it doesn’t actually make a copy but just returns the same one.

For example, for all (?) the numeric types in `Base`, `b === copy(b)`.

(Also, if performance is at all a consideration this really should be a parametric type)

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [March 26, 2024, 2:16pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/3 "2024-03-26T14:16:41Z")

</div>

This was just an example (in fact the one from the docs) to demonstrate what I want to do. In the actual use case the fields of the structs are arrays (hence mutable) and I am using parametric types. But adding a second inner constructor gave me an idea how to do it:

```julia
struct OrderedPair
    x::Real
    y::Real
    OrderedPair(x, y, safe::Val{True}) = new(op.x, op.y)
    OrderedPair(x, y, safe::Val{False}) = x > y ? error("out of order") : new(x,y)
    OrderdPair(x, y) = OrderedPair(x, y, Val(false)) # could probably also have this as an outer constructor
end

```

and now I can do

```julia
Base.copy(op::OrderedPair) = OrderedPair(op.x, op.y, Val(true))

```

to achieve what I wanted.

---

<div class="post-metadata">

**Author:** ![Dan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dan/32/42581_2.png) [@Dan](https://discourse.julialang.org/u/Dan)\
**Post date:** [March 26, 2024, 2:36pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/4 "2024-03-26T14:36:57Z")

</div>

Another option is to use `reinterpret`. I like this method because it emphasizes the danger of bypassing the inner constructor checks:

```julia
struct OrderedPair{T <: Real}
    x::T
    y::T
    OrderedPair(x, y) = x > y ? error("out of order") : 
      new{promote_type(typeof(x),typeof(y))}(x,y)
end

```

with this definition, the normal construction works as expected:

```julia
julia> OrderedPair(1,2)
OrderedPair{Int64}(1, 2)

julia> OrderedPair(1,2.2)
OrderedPair{Float64}(1.0, 2.2)

julia> OrderedPair(2,1)
ERROR: out of order

```

And the unsafe construction can be done as follows:

```julia
julia> reinterpret(OrderedPair{Int}, [2,1])[1]
OrderedPair{Int64}(2, 1)

```

---

<div class="post-metadata">

**Author:** ![Zach\_Christensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zach_christensen/32/7220_2.png) [@Zach\_Christensen](https://discourse.julialang.org/u/Zach_Christensen)\
**Post date:** [March 26, 2024, 4:01pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/5 "2024-03-26T16:01:19Z")

</div>

I’ve started to define an internal constructor that is “unsafe” any time I have a new type defined.

```julia
struct OrderedPair{T <: Real}
    x::T
    y::T

    global unsafe_oredered_pair(x::T, y::T) = new{T}(x, y)
end
function OrderedPair(x::T, y::T) where {T}
    x > y && error("out of order")
    unsafe_oredered_pair(x, y)
end
OrderedPair(x, y) = OrderedPair(promote(x, y)...)

```

This way you can freely skip checking conditions when you know they’ve been met elsewhere in your code without moving everything to an inner constructor. You can name the inner constructor whatever you want but the naming convention ensures downstream users don’t have any excuses for inappropriately bypassing checks.

---

<div class="post-metadata">

**Author:** ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)\
**Post date:** [March 26, 2024, 4:12pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/6 "2024-03-26T16:12:20Z")

</div>

Oh, that seems even better than my solution!

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [March 26, 2024, 4:19pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/7 "2024-03-26T16:19:48Z")

</div>

If you define it within in the struct, you are allowed to use `new` even in things that are not constructors  
e.g.

```julia
julia> struct OrderedPair
          x::Real
          y::Real
          OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
          
          Base.copy(op::OrderedPair) = new(op.x, op.y)
       end

julia> op1 = OrderedPair(10, 20)
OrderedPair(10, 20)

julia> copy(op1)
OrderedPair(10, 20)

julia> @which copy(op1)
copy(op::OrderedPair)
     @ Main REPL[1]:6

```

Same principle as what @Zach_Christensen did.  
But forcing it to be kept near the defintion so noone can just call things.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [March 26, 2024, 4:28pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/8 "2024-03-26T16:28:56Z")

</div>

Significantly more evily you can directly generate calls to `Expr(:new, T, args...)` and to `Expr{:splatnew, T, args)` e.g. with generated functions or other metaprogramming.

```julia
julia> struct OrderedPair
          x::Real
          y::Real
          OrderedPair(x,y) = x > y ? error("out of order") : new(x,y)
       end

julia> @generated direct_new(B::Type, args...) = Expr(:splatnew, :B, :args)
direct_new (generic function with 1 method)

julia> Base.copy(op::OrderedPair) = direct_new(OrderedPair, op.x, op.y)

julia> op1 = OrderedPair(10, 20)
OrderedPair(10, 20)

julia> copy(op1)
OrderedPair(10, 20)

julia> @which copy(op1)
copy(op::OrderedPair)
     @ Main REPL[3]:1

```

Should you?  
idk, I am not your mum.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [March 26, 2024, 4:40pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/9 "2024-03-26T16:40:28Z")

</div>

> [@screw\_dog](#):
>
> However, this generally won’t do what you want because since this is immutable and most `Real` types are also immutable, it doesn’t actually make a copy but just returns the same one

I’m not seeing the problem here. For immutable number types, the desired behavior should be achieved both with and without the inner `copy`.

---

<div class="post-metadata">

**Author:** ![screw\_dog](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/screw_dog/32/48119_2.png) [@screw\_dog](https://discourse.julialang.org/u/screw_dog)\
**Post date:** [March 27, 2024, 12:55am UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/10 "2024-03-27T00:55:23Z")

</div>

> I’m not seeing the problem here

Not a problem, per se, but just that in the situation as posed in the first post there’s no need for bypassing the inner constructor as you could just define `copy(op::OrderedPair) = op` and move on.

It was only in the next post that it was clarified that the actual use case was more complex.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mnemnion/32/206596_2.png) [@mnemnion](https://discourse.julialang.org/u/mnemnion)\
**Post date:** [March 27, 2024, 4:20pm UTC](https://discourse.julialang.org/t/bypass-inner-constructor-call-new-outside-inner-constructor/112142/12 "2024-03-27T16:20:18Z")

</div>

> [@jlbosse](#):
>
> ```julia
> struct OrderedPair
> x::Real
> y::Real
> OrderedPair(x, y, safe::Val{True}) = new(op.x, op.y)
> OrderedPair(x, y, safe::Val{False}) = x > y ? error("out of order") : new(x,y)
> OrderdPair(x, y) = OrderedPair(x, y, Val(false)) # could probably also have this as an outer constructor
> end
> 
> ```

Minor stylistic point, but you’re using the `Val` here as a trait (sort of, you’re using it for compile-time dispatch on a type), so it should probably come first in order:

```julia
struct OrderedPair
    x::Real
    y::Real
    OrderedPair(::Val{True}, x, y) = new(op.x, op.y)
    OrderedPair(::Val{False}, x, y) = x > y ? error("out of order") : new(x,y)
    OrderdPair(x, y) = OrderedPair(Val(false), x, y) # could probably also have this as an outer constructor
end

```

This also shows the convention that arguments which aren’t directly referenced because they’re used for their type don’t receive names.

Nothing bad will happen the way you wrote it, of course, this is just a matter of idiom.
