# Proposed alias for union types

**URL:** <https://discourse.julialang.org/t/proposed-alias-for-union-types/108205>\
**Category:** Internals & Design\
**Tags:** question, proposal\
**Created:** [December 31, 2023, 1:43pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205 "2023-12-31T13:43:56Z")\
**Posts on this page:** 20\
**Page:** 5

<div class="post-metadata">

**Author:** ![xiaoxi](https://avatars.discourse-cdn.com/v4/letter/x/a9adbd/32.png) [@xiaoxi](https://discourse.julialang.org/u/xiaoxi)\
**Post date:** [January 3, 2024, 9:34am UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/81 "2024-01-03T09:34:23Z")

</div>

It might be interesting to know if the developers of Cthulhu.jl and the VS Code extension would be interested in adopting one of the syntactic sugars and see if they can get feedback on it.

Could there be a poll for people to choose which option they prefer?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 3, 2024, 9:41am UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/82 "2024-01-03T09:41:56Z")

</div>

> [@CameronBieganek](#):
>
> But perhaps I’m reading too much into that page and they should not be considered “interfaces”. 😂

Collections on that page predate the formalization of interfaces (like abstract arrays, iteration, broadcasting). I agree that it is confusing, but there are no strong incentives to clean it up since people get by with informal definitions.

In retrospect, **packages are the best place to evolve interfaces** — [Tables.jl](https://github.com/JuliaData/Tables.jl) is a great example of how well that works, but also how long it takes to refine interfaces (6 years and still ongoing). Maybe one day someone will formalize collections.

Incidentally, I think that **the `|` syntax for type unions should be explored in a package** too if that is feasible. Discussions about features are helpful, but some corner cases only appear with usage. It is of course technically type piracy, but as long as that is clarified I see no problem.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [January 3, 2024, 10:28am UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/83 "2024-01-03T10:28:37Z")

</div>

> [@Tamas\_Papp](#):
>
> Incidentally, I think that **the `|` syntax for type unions should be explored in a package** too if that is feasible

It is of course possible to use in a package. It is very feasible in a single-user project.

However, nobody sane should use that for “real” projects: The upside in terms of improved syntax is small, and this imposes a real “weirdness tax” on new developers / users of your project.

Saying “do this in packages” is mostly equivalent to “don’t do this”, due to network effects.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 3, 2024, 10:37am UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/84 "2024-01-03T10:37:39Z")

</div>

> [@Tamas\_Papp](#):
>
> the `|` syntax for type unions should be explored in a package

I created a package at [GitHub - mkitti/OrUnions.jl: Julia package to evaluate alternate Union syntax via | and ∨](https://github.com/mkitti/OrUnions.jl) . To avoid piracy I used `∨` (\vee) rather than `|`.

```julia
julia> 0x5::(UInt8 ∨ Int8)
0x05

julia> f(x::(Int8 ∨ UInt8 ∨ Int)) = x
f (generic function with 1 method)

```

If you really want to use `|`, I created a macro called `@orunion`.

```julia
julia> @orunion function foo(::Int8 | UInt8 | Int16) end
foo (generic function with 1 method)

julia> methods(foo)
# 1 method for generic function "foo" from Main:
 [1] foo(::Union{Int16, Int8, UInt8})
     @ REPL[34]:1

julia> @macroexpand @orunion function foo(::Int8 | UInt8 | Int16) end
:(function foo(::Union{Int16, UInt8, Int8})
      #= REPL[36]:1 =#
      #= REPL[36]:1 =#
  end)

julia> @orunion bar(x::Int8 | UInt8, y::Int16 | UInt16, z::(Int8 | UInt8) | Int) = nothing
bar (generic function with 1 method)

julia> methods(bar)
# 1 method for generic function "bar" from Main:
 [1] bar(x::Union{Int8, UInt8}, y::Union{Int16, UInt16}, z::Union{Int64, Int8, UInt8})
     @ REPL[38]:1

julia> @macroexpand @orunion bar(x::Int8 | UInt8, y::Int16 | UInt16, z::(Int8 | UInt8) | Int) = nothing
:(bar(x::Union{UInt8, Int8}, y::Union{UInt16, Int16}, z::Union{Int, Union{UInt8, Int8}}) = begin
          #= REPL[40]:1 =#
          nothing
      end)

julia> methods(@orunion (x::Int8 | UInt8)->5)
# 1 method for anonymous function "#35":
 [1] (::var"#35#36")(x::Union{Int8, UInt8})

```

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [January 3, 2024, 4:42pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/85 "2024-01-03T16:42:26Z")

</div>

This is great @mkitti, nice work.

I also wanted to append a couple of favorite examples after trying out an infixed type union on some of my code –

1. A common workflow in data science is to have tables with missing values, which means you might have some table schema that look like this:

```julia
julia> typeof(schema((a=[1f0, missing], b=[missing, "hello"], c=[1, missing])))

ScientificTypes.Schema{
    (:a, :b, :c),
    Tuple{Union{Continuous,Missing},Union{Textual,Missing},Union{Count,Missing}},
    Tuple{Union{Float32,Missing},Union{String,Missing},Union{Int64,Missing}},
}

```

With an infix operator, this gets simplified to:

```julia
ScientificTypes.Schema{
    (:a, :b, :c),
    Tuple{(Continuous|Missing),(Textual|Missing),(Count|Missing)},
    Tuple{(Float32|Missing),(String|Missing),(Int64|Missing)},
}

```

I find it’s easier to parse like this. Not only is it much less verbose, but I feel like it is structurally clearer. Maybe this is because you can clearly identify the different containers from their style – that being `(A|B)` for unions and `MyType{A,B}` for parametrized types. Whereas before this, squinting at the page, `Union{A,B}` looks like it could be another parametrized type that contains both an `A` and a `B` for some of its fields. But `Union` is special in that its type parameters are not all used in a single instantiation – so the visual cue really helps.

2. Setting defaults is easier to read:

Before:

```julia
f(; cleanup::Union{Bool,Nothing}=nothing) = ...

```

which becomes

```julia
f(; cleanup::(Bool|Nothing)=nothing) = ...

```

I like that `Bool` appears right next to `cleanup`. Before this, the first thing I see is that `cleanup` is a `Union`, and after parsing one more word I see that it could be a `Bool`. But after this, I first read that `cleanup` is a `Bool`, and the next symbol is “or this other thing”.

But stylistically the choice could be context-dependent. Maybe you would want to very explicitly inform a user that it is a `Union` – so you could just use that instead.

---

<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:** [January 3, 2024, 5:06pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/86 "2024-01-03T17:06:45Z")

</div>

I think it’s really helpful to have real-world examples like those! In my experience — and looks like yours, too — `Union`s are most common as either `Union{T, Missing}` or `Union{T, Nothing}`. Personally, I’d aim for a solution that is more laser-focused at those use-cases. For example, it looks like a post-fix `?` operator would be _marginally_ available and might even be customizable. There could exist some future Julia world in which this would work:

```julia-repl
julia> using Missings: ?

julia> Int?
Union{Missing, Int64}

julia> typeof(schema((a=[1f0, missing], b=[missing, "hello"], c=[1, missing])))
ScientificTypes.Schema{
    (:a, :b, :c),
    Tuple{Continuous?, Textual?, Count?},
    Tuple{Float32?, String?, Int64?},
}

#=--- or even ---=#
julia> var"?"(::Type{T}) where {T} = Union{T,Nothing}
? (generic function with 1 method)

julia> String?
Union{Nothing, String}

julia> Meta.@lower T?
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope`
1 ─ %1 = var"?"(T)
└── return %1
))))

```

This is would match how DataFrames _currently_ uses the `?` symbol in its output:

```julia-repl
julia> df = DataFrame(a=[1,missing], b=["a",missing], c=[1.0,missing])
2×3 DataFrame
 Row │ a b c
     │ Int64? String? Float64?
─────┼─────────────────────────────
   1 │ 1 a 1.0
   2 │ missing missing missing

```

See: [Allow '?' in variable and function names · Issue #22025 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/22025#issuecomment-867560494) and [Use ? to lift missing values · Issue #36628 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/36628)

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [January 3, 2024, 5:45pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/87 "2024-01-03T17:45:11Z")

</div>

`? == Union{T,Missing}` would be great also; it’s extremely intuitive! But I’m afraid I may not have the energy for another one of these threads; maybe someone else could carry the torch for that 😅

I’d be more in favor of just simplifying all `Union`s at once. Especially given that `|` has such a strong precedent in other languages, and is not quite as frequently used as the ternary operator.

* * *

By the way, I really want to emphasize the point I made in the comment above, since I didn’t think about it until now. But it helped convince myself that we should try `|` even more:

`Union` is a unique type in that **not all of its type parameters are used for a single instantiation**

When you look at a container type like `Container{A,B,C}`, you might infer that each of `A`, `B`, and `C` are all used in the fields and necessary (not always, but often). But `Union` is a special type in that only one of its type parameters is used at a time! Or within a single method definition.

Thus, I think some sort of structural visual cue really helps. For example:

```julia
function f(x::Tuple{Union{Float64,Int64},MyType{<:Union{Float64,Nothing}}})
    a, b = x
    ...
end

```

If you are skimming code at 2am, it’s takes an extra second to parse that `a` could be a `Float64` OR a `Int64` – rather than both those types appearing somewhere. But compare this with a different visual cue for unions:

```julia
function f(x::Tuple{(Float64|Int64),MyType{<:(Float64|Nothing)}})
    a, b = x
    ...
end

```

to me, this **highlights the variable parts** in the signature, so in my head I can quickly imagine the behavior for different type combinations.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 3, 2024, 5:49pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/88 "2024-01-03T17:49:54Z")

</div>

> [@mbauman](#):
>
> `Union`s are most common as either `Union{T, Missing}` or `Union{T, Nothing}`. Personally, I’d aim for a solution that is more laser-focused at those use-cases.

Personal opinion, but I somewhat strongly disagree here. Even if those cases are common, it feels really weird to privilege them in the parser, when there’s lots of other domains and use cases where people want to talk about other types of unions, and the conflict between `Missing` and `Nothing` isn’t likely to be resolved anyways.

> [@mbauman](#):
>
> I’d aim for a solution that is more laser-focused at those use-cases. For example, it looks like a post-fix `?` operator would be _marginally_ available and might even be customizable.

This one seems really unlikely to me. The ternary operator `a ? b : c` is already such a big source of confusion and parser quirks, and this would make it even worse. E.g.

```julia
a?
b : c

```

would mean something completely different from

```julia
a ?
b : c

```

if that operator was added.

---

<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:** [January 3, 2024, 5:58pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/89 "2024-01-03T17:58:00Z")

</div>

The ternary `? :` syntax requires spaces everywhere to work. `a?` is a syntax error no matter how you break it up:

```julia-repl
julia> a?
ERROR: syntax: space required before "?" operator
Stacktrace:
 [1] top-level scope
   @ none:1

```

This could be done without special casing either Nothing or Missing. All it’d do is parse `T?` akin to `T'` — and just like LinearAlgebra defines the post-fix `'` to mean adjoint (transpose) while Diffractor uses it for adjoint (derivative), this could be available for packages and users to define as they wish. Note that this can extend beyond types to functions, to allow “lifting” functions similarly.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 3, 2024, 5:59pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/90 "2024-01-03T17:59:48Z")

</div>

> [@mbauman](#):
>
> The ternary `? :` syntax requires spaces everywhere to work. `a?` is a syntax error no matter how you break it up:

currently yes, but your proposal is that the `a?` case would no longer throw an error. So

```julia
a ?
b : c

```

becomes

```julia
if a
    b
else
    c
end

```

whereas

```julia
a?
b : c

```

would parse to

```julia
Union{a, Missing}
(:)(b, c)

```

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 3, 2024, 6:38pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/91 "2024-01-03T18:38:39Z")

</div>

If you follow the link into the discussion in other langauges, you also see a discussion of using `?` for an optional syntax.

- [That said I’d rather not introduce new syntax (like `T?` or `?T`) for  
 `Optional[T]` – let’s see what we can do with the existing operators. - Guido van Rossom](https://mail.python.org/archives/list/python-ideas@python.org/message/2DOPEXLV3ABRR2ACLCWTK4NVSTWNDOZN/)
- [js.| union type · Issue #1824 · scala-js/scala-js · GitHub](https://github.com/scala-js/scala-js/issues/1824#issuecomment-125629282)

With that in mind there is also

```julia
julia> const Optional{T} = Union{T,Nothing}
Union{Nothing, T} where T

julia> Optional{Float64}
Union{Nothing, Float64}

julia> const Maybe{T} = Union{T,Missing}
Union{Missing, T} where T

julia> Maybe{Int}
Union{Missing, Int64}

```

---

<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:** [January 3, 2024, 6:51pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/92 "2024-01-03T18:51:56Z")

</div>

This thread is long enough to see where minds are settling.

_If it is necessary to implement a change and add `|`, `?` or both, what would be the best action?_

_Poll ([view on site](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/92))_

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 3, 2024, 8:20pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/93 "2024-01-03T20:20:02Z")

</div>

Regarding `?` as a prefix, do note the [current REPL tab-completion feature](https://docs.julialang.org/en/v1/stdlib/REPL/#Tab-completion) for listing methods that apply to a set of arguments.

```julia
julia> ?(5, 3.0, nothing)[TAB]
Slices(A::P, slicemap::SM, ax::AX) where {P, SM, AX} @ Base slicearray.jl:41
broadcast(f::Tf, As...) where Tf @ Base.Broadcast broadcast.jl:811
broadcast!(f::Tf, dest, As::Vararg{Any, N}) where {Tf, N} @ Base.Broadcast broadcast.jl:850
clamp(x::X, lo::L, hi::H) where {X, L, H} @ Base.Math math.jl:89
error(s::Vararg{Any, N}) where N @ Base error.jl:42
hvcat(nbc::Int64, as...) @ Base abstractarray.jl:2011
hvncat(dim::Int64, xs...) @ Base abstractarray.jl:2259
methods(f, t, mod::Union{Nothing, Tuple{Module}, AbstractArray{Module}}) @ Base reflection.jl:988

```

While related, I think the special case for `Nothing` or `Missing` issue is separable from the `|` for type unions issue in that they could be discussed and implemented via independent tracks.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 3, 2024, 9:37pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/94 "2024-01-03T21:37:54Z")

</div>

It seems that if folks insist on adding an infix operator for `Union`, it should be `∪`. The precise interpretation of the `∪` docstring can be debated, but at least the overall meaning of `∪` is “union” rather than “bitwise-or”.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 3, 2024, 9:45pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/95 "2024-01-03T21:45:11Z")

</div>

> [@Dan](#):
>
> This thread is long enough to see where minds are settling.

It would be good to temper expectations here — realize that there is a big difference between getting a bunch of self-selected participants to agree on a discourse thread/poll and getting a pull request merged.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 3, 2024, 9:59pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/96 "2024-01-03T21:59:42Z")

</div>

> [@CameronBieganek](#):
>
> It seems that if folks insist on adding an infix operator for `Union`, it should be `∪`. The precise interpretation of the `∪` docstring can be debated, but at least the overall meaning of `∪` is “union” rather than “bitwise-or”.

For an infix operator for `Union`, my second choice would be `∨` rather than `∪` based on the [Curry-Howard Isomorphism above I discussed above](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/80) and Howard’s 1980 paper. I could not find as clear a precedent for `∪`.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 3, 2024, 10:04pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/97 "2024-01-03T22:04:13Z")

</div>

> [@mbauman](#):
>
> There could exist some future Julia world in which this would work:

The closest current Julia syntax I could come up with is as follows.

```julia-repl
julia> macro question_mark(ex)
           return Expr(:curly, :Union, ex, :Missing)
       end
@question_mark (macro with 1 method)

julia> const var"@?" = var"@question_mark"
@question_mark (macro with 1 method)

julia> @? Int8
Union{Missing, Int8}

```

---

<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:** [January 3, 2024, 10:06pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/98 "2024-01-03T22:06:06Z")

</div>

I mean, changes like these — surface, superficial, as-little-as-1-line-of-code, but super visible — are _always_ hard to come to consensus. Everyone has an opinion — and obviously opinions vary.

The premise behind the poll is indeed flawed — it’s definitively **not** necessary to implement any change here and “insisting” on a change isn’t what makes change happen. The status quo works and rules supreme. Could it be better? Perhaps, but again, opinions on what makes “better” vary. In fact, **all** secondary forms here have the drawback of being yet-another-thing-to-learn (and likely hard to search for!).

---

<div class="post-metadata">

**Author:** ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)\
**Post date:** [January 3, 2024, 10:08pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/99 "2024-01-03T22:08:24Z")

</div>

Is a PR with a formal proposal from the interested parties the right next step then? We’ve certainly reached a length where it’s evidently hard for people to read the entire thing. I’m wary of this as we are drifting into a meta-discussion about the true meaning of forum polls…

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 3, 2024, 10:28pm UTC](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205/100 "2024-01-03T22:28:29Z")

</div>

> [@mkitti](#):
>
> Several here have proposed using `∪`, the set theory union. Note the visual similarity to `∨`. Apparently [this is not a coincidence](https://math.stackexchange.com/questions/3867554/the-formal-difference-between-cup-and-vee). However, Curry-Howard refers to logic not sets.

I don’t really understand this as a disqualifier for `∪`. There’s also an isomorphism between the union of sets and the union of types, and that isomorphism is arguably more straightforward than the one between structural logic and types, so I don’t see why that would be used to disqualify `∪`.

Maybe the isomorphism doesn’t have as fancy a name though, so that may be a downside.

[Previous page](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205.md?page=4)

[Next page](https://discourse.julialang.org/t/proposed-alias-for-union-types/108205.md?page=6)
