# Should Any have an identity constructor method?

**URL:** https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500
**Category:** Internals & Design
**Tags:** type, design, constructors, methods
**Created:** [January 31, 2024, 8:19am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500 "2024-01-31T08:19:48Z")
**Posts on this page:** 11
**Page:** 1

<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: [January 31, 2024, 8:19am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/1 "2024-01-31T08:19:48Z")

</div>

Many non-concrete types have constructors, for example we have `AbstractFloat(0.3) == 0.3` and `Union{Int,Float64}(0.3) == 0.3`. Should we also have a method like `Any(x) = x`? Then we would have `map(Any, [3,4,5]) == map(identity, [3,4,5])`.

Xref: [generalize the `Number` identity constructor by nsajko · Pull Request #53130 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/pull/53130)

---

<div class="post-metadata">

### Author: ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)
#### Post date: [January 31, 2024, 8:30am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/2 "2024-01-31T08:30:31Z")

</div>

Is there any real application to that? E.g. are some codes producing error messages?

Usually, one should anyway prefer `convert(Any, x)` to convert to another datatype.

---

<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: [January 31, 2024, 8:32am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/3 "2024-01-31T08:32:42Z")

</div>

Adding such a method would make Julia a bit more internally consistent, so a bit friendlier to those who are still learning the language.

---

<div class="post-metadata">

### Author: ![SteffenPL](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/steffenpl/32/206270_2.png) [@SteffenPL](https://discourse.julialang.org/u/SteffenPL)
#### Post date: [January 31, 2024, 8:36am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/4 "2024-01-31T08:36:22Z")

</div>

I just saw the GitHub PR. However, it is unclear to me if the right answer would be in that case that one should use `convert` instead of assuming a constructor exists.

E.g. referring to your PR, the following works already

```julia
 convert(Union{String, Int}, 4)

```

I could imagine that there are many more corner cases where a constructor is missing for some weird type or intentionally not allowed, but it is reasonable to have a `convert`.

---

<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: [January 31, 2024, 8:37am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/5 "2024-01-31T08:37:33Z")

</div>

Yes, `convert` exists, but TBH that’s not really relevant to this topic, as we already _do_ have many non-concrete constructors.

---

<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: [January 31, 2024, 9:44am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/6 "2024-01-31T09:44:14Z")

</div>

We need to clearly distinguish conversion behavior and identity behavior, which is a little weird because `convert` does both.

For `convert`s where the input type does not match the target type, `convert` has conversion methods. For `Number`s, `convert(::Type{T}, x::Number) where {T<:Number} = T(x)::T` forwards to the specified type’s constructor that implements the conversions. Note this includes defined `Number` subtypes like `AbstractFloat`, not type unions that aren’t specifically defined, so pushing `1` to a `Vector{Union{Float32, Float64}}` errors even though it would work for a `Vector{Float32}` or `Vector{Float64}`.

For `convert`s where identity behavior occurs, `convert` has identity-like methods. I don’t know why there’s so many, but there’s one for `Type{T}`, `Any`, and `Number` in `Base`.

The constructors with identity behavior like `Union{Int,Float64}(0.3)`, `AbstractFloat(0.3)`, and even `Float64(0.3)` actually use `(::Type{T})(x::T) where {T<:Number} = x` from boot.jl in `Core`. These are not used by `convert` with identity behavior. It makes sense for this fallback to exist because it’d be strange if the specifically defined types’ constructors only worked on different types, but again this additionally covers type unions.

Taking all that together, it seems like the identity behavior is only as flexible and justified as it is for `Number`s where constructors also implement type conversion. There’s no reason to replicate this identity behavior for other types like `Function` and `Any` where you _don’t_ want conversion e.g. what on earth would `Function(0.3)` do? `Any` being the universal supertype shouldn’t ever convert, sure, but there’s no reason to do this when `convert` exists to handle both conversion and identity behavior for all types. It’s informative to let `Function` and `Any` lack any constructors like other defined abstract types by default, even printing so in the `MethodError` instead of listing close method matches.

---

<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: [January 31, 2024, 9:51am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/7 "2024-01-31T09:51:16Z")

</div>

> [@Benny](#):
>
> We need to clearly distinguish conversion behavior and identity behavior, which is a little weird because `convert` does both. […] It’s informative to let `Function` and `Any` lack any constructors like other defined abstract types by default, even printing so in the `MethodError` instead of listing close method matches.

I’m confused. Why are you bringing up `convert` and `Function`? This seems completely off-topic.

BTW I’m definitely _not_ advocating for `Function(0.3)` to do anything but **throw** (`0.3 isa Function` doesn’t hold), as it already does.

---

<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: [January 31, 2024, 9:58am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/8 "2024-01-31T09:58:58Z")

</div>

Because SteffenPL brought up `convert(Any, x)` for type conversion, and `Function` is a constructor-less abstract type like `Any` is. `Function` and `Any` also happen to share the limitation of being disallowed type annotations of the callable in a method definition. If you’re saying that type conversion is unrelated to identity, that’s my point. `convert(Any, x)` does not perform type conversion as SteffenPL said, it uses an identity-like method.

Despite that, I agree that `identity` should be used for identity and `convert` should be used for both conversion and identity (which is something you want for generic functions), instead of adding identity constructors beyond `Number`. Feature bloat is already bad, this would be non-feature bloat because we already have clearer and better ways. It wouldn’t actually be consistent for the language because 1) it’s normal for behaviors to be limited to an abstract type like `Number` and all its subtypes, 2) `Any` shouldn’t ever convert inputs like `Number` subtypes do, 3) identity constructors would still not exist for most subtypes of `Any`, which would lack the consistency of `Number` described in (1).

---

<div class="post-metadata">

### Author: ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)
#### Post date: [January 31, 2024, 10:22am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/9 "2024-01-31T10:22:01Z")

</div>

This would be breaking to add in general, because constructors should create _new_, independent instances, e.g.:

```julia
julia> v = [1,2,3];

julia> v2 = Vector{Int}(v);

julia> v === v2
false

```

So having `(::Type{T1})(x::T2) where {T1, T2 <: T1} = x` would at the very least violate this rule, and perhaps even be breaking. It’s fine for `Number` because `Number`s are immutable (this is undocumented, but it’s assumed to be true in Julia), and so new instances are indistinguishable from old ones.

---

<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: [January 31, 2024, 10:25am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/10 "2024-01-31T10:25:11Z")

</div>

Technically mutable `Number`s are an important minority, though it beats me how you mutate them:

```julia
julia> x = BigInt(3)
3

julia> y = BigInt(x) # identity constructor
3

julia> x === y
true

julia> ismutable(x)
true

```

---

<div class="post-metadata">

### Author: ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)
#### Post date: [January 31, 2024, 10:31am UTC](https://discourse.julialang.org/t/should-any-have-an-identity-constructor-method/109500/11 "2024-01-31T10:31:00Z")

</div>

`BigInt` and `BigFloat` are semantically immutable, meaning they can technically be mutated by reaching into internal functions, but Base Julia assumes one never does that. This is a dumb, undocumented gotcha in Julia, and based on the idea that `Number`s are always immutable (despite their implementation).

For example

```julia
julia> f() = big"1" # always returns 1
f (generic function with 1 method)

julia> x = Base.GMP.MPZ.add!(f(), big"2")
3

julia> f() # oops
3

```
