Why is there a type "Pair"?

I recently learned of the existence of the => operator, which forms an element of type Pair:

julia> 2 => ones(4)
2 => [1.0, 1.0, 1.0, 1.0]

which makes a Pair. I just assumed Pair was a convenience type synonym for a length 2 Tuple, but it’s not.

julia> typeof((1,2)) == typeof(1 => 2)
false

Just curious as to what functionality Pair provides that a two element tuple could not ?

2 Likes

A 2-tuple is a collection of elements that may have nothing to do with each other; a pair associates the first element with the second. It’s typically used to relate dictionary keys with their respective values, for instance.

Regarding functionality differences, as their docstring tell, a Pair is treated as a single “scalar” for broadcasting operations.

2 Likes

The reason we need a difference is to facilitate broadcast. Broacasting over (1,2) applies to the two elements separately, but broadcasting treats 1=>2 as a scalar.

4 Likes

No, the Pair type came long before the dot syntax for broadcasting.

2 Likes

See here for a previous (short) discussion: https://discourse.julialang.org/t/pair-a-b-vs-tuple-a-b/

1 Like

That’s not the right question in Julia. All 2-element collections could do the same thing.

But, of course, you may not want them to do the same thing, and having a different type allows different semantics using dispatch. Specifically, key => value is used extensively in Julia.

Generally those are done using a type alias.

3 Likes

cough arrays cough

1 Like

Of course pairs and 2-tuples are structurally the same, and 2-tuples can just as well represent an association from the first item to the second item (that is the definition of a mathematical functional relation after all: [1], [2], [3], [4], [5], [6]).

The reason for the distinction is to clarify our intentions about using them: you use Pair when you want to represent a functional relation, you use Tuple when you want an ordered container. This way you reduce the chances of messing things up inadvertently.

It is same reason why you might want to introduce two structurally equivalent types

struct Meters{R<:Real} val::R end
struct Kilograms{R<:Real} val::R end

just so that you cannot accidentally confuse them and add Meters(3.14) + Kilograms(42.).

The newtype pattern ([7]) is based on this principle: you want a type which has the same underlying representation, but can behave differently.

4 Likes