How about direct sum ⊕ as vector attach operator?

Background

I know that Julia’s design is very reasonable, especially from the viewpoint of mathematics; it’s my favorite feature of Julia. For example, Julia uses * as a string join operator instead of +, unlike many programming languages, because it makes sense in the context of (abstract) algebra:

\text{"hello"} \ast \text{"world"} = \text{"helloworld"}

is much more natural than

\text{"hello"} + \text{"world"} = \text{"helloworld"}

obviously. But sometimes I miss some operators in Python. In this context, + serves as a vector concatenator.

Python 3.11.4 (tags/v3.11.4:d2340ef, Jun  7 2023, 05:45:37) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> x = [1,2]
>>> y = [3,4]
>>> x + y
[1, 2, 3, 4]

Of course, I think vcat(x, y) or [x; y] in Julia are also great.

julia> x = [1,2]
2-element Vector{Int64}:
 1
 2

julia> y = [3,4]
2-element Vector{Int64}:
 3
 4

julia> vcat(x, y)
4-element Vector{Int64}:
 1
 2
 3
 4

julia> [x; y]
4-element Vector{Int64}:
 1
 2
 3
 4

I understand that this grammatical consistency is important for reading or writing code. Most significantly, I know that x + y is totally weird in a mathematical sense.

Then how about \oplus? Usually, in general mathematics, a direct sum \oplus (\oplus) refers to a Cartesian product of vector space or some algebraic structures. For instance, the plane \mathbb{R}^{2} and the z-axis \mathbb{R}^{1} could be merged in 3-dimensional space \mathbb{R}^{3} by \mathbb{R}^{3} = \mathbb{R}^{2} \oplus \mathbb{R}^{1}. In a coordinate system, it’s exactly the same as vector concatenation, (x, y, z) = (x, y) \oplus (z).

Implementation

Below are simple implementations. If you love these, you can use them personally even if Julia doesn’t change. I believe all of you guys have a personal library, just for yourselves. :joy:

Binary operation

Please note that I didn’t check for performance issues. vcat or other ways could be more efficient and fast.

julia> ⊕(x, y) = [x; y]
⊕ (generic function with 1 method)

julia> [1, 2] ⊕ [3, 4]
4-element Vector{Int64}:
 1
 2
 3
 4

\bigoplus_{k} A_{k}

Since \oplus is a binary operation, we can generalize it using reduce.

julia> reduce(⊕, [[1, 2], [3, 4], [5, 6, 7]])
7-element Vector{Int64}:
 1
 2
 3
 4
 5
 6
 7

The following is a method form.

julia> ⊕(x, y...) = reduce(⊕, [x, y...])
⊕ (generic function with 2 methods)

julia> ⊕([1, 2], [3, 4], [5, 6, 7])
7-element Vector{Int64}:
 1
 2
 3
 4
 5
 6
 7

Clean code

⊕(x, y) = [x; y]
⊕(x, y...) = reduce(⊕, [x, y...])

Any comments or ideas are helpful to me. Thank you for your attention!

2 Likes

You can of course introduce this definition for the operator in your own code or package, but adding it to Julia Base is likely not a good idea, at least not as an exported symbol. Doing so would break most code that already defines this operator for themselves. Here’s a list of registered packages that already make use of this operator:
https://juliahub.com/ui/Search?q=⊕&type=symbols

9 Likes

Base is allowed to export new names; it won’t break any code that defines or imports this operator, only code where there is ambiguity.

seems fine if that’s what it means — are there other mathematical usages of that would conflict with this meaning?


Looking through the other languages, I see infix operators:

,
++
∾
+
~
&
@
|||
<<

of which Julia has available

++
∾
~
@
|||

It would be nice to even have a function to concatenate sequences. vcat maybe should’ve been that but it does weird stuff a lot of the time.

julia> vcat("a", "b")
2-element Vector{String}:
 "a"
 "b"

I’d be happy enough with an append or concat.

1 Like

Cf. https://github.com/JuliaLang/julia/issues/11030 and RFC: deprecate * in favor of new ++ concatenation operator by stevengj · Pull Request #22461 · JuliaLang/julia · GitHub for some related old discussions.

1 Like

Even if “base is allowed to export new names”, it might not be a good idea and in the best interest of the users of the language.

And it does break importing code

julia> module Foo
       export sin
       sin(x) = 0
       end
Main.Foo

julia> using .Foo

julia> sin(2)
WARNING: both Foo and Base export "sin"; uses of it in module Main must be qualified
ERROR: UndefVarError: `sin` not defined
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

That’s the ambiguous case. The unambiguous case is

julia> module Foo
              export sin
              sin(x) = 0
              end
Main.Foo

julia> using .Foo: sin

julia> sin(2)
0
2 Likes
3 Likes