# How to construct infix operator with usual behavior?

I am trying to define my own infix operator and having problems with strung-together evaluation. The code below is a simple example. What I would like is to have the operator treated associatively, but that fails.

(* define our own dot product *)

x_⊙y_ := x.y

(* and some tensors *)

x = {4, 5};

y = {{1, 2}, {3, 4}};

z = {{2, 3}, {3, 4}};

(* the usual dot product evaluates in order *)

z.y.x

(* {124,170} *)

(* but ours does not *)

z⊙y⊙x

(* {{2,3},{3,4}}⊙{{1,2},{3,4}}⊙{4,5} *)


Look at the attributes of Dot:

Attributes[Dot]


{Flat, OneIdentity, Protected}

The combination of Flat and OneIdentity is what takes care of associativity for Dot, so do the same with CircleDot:

SetAttributes[CircleDot, {Flat, OneIdentity}];
CircleDot[x_, y_] := x . y


Now, CircleDot should have the associativity you desire:

z ⊙ y ⊙ x


{124, 170}

1. I think this is the canonical way to create an associative operator.

2. If you want to create a left-associative or right-associative operator, then you do not want to use the Flat attribute. An operator that is Flat will satisfy:

f[x, y, z] === f[f[x, y], z] === f[x, f[y, z]]


which is clearly neither left nor right associative.

1. Operators like LeftTee achieve left-associativity through parsing. For example, the input:

Hold[x ⊣ y ⊣ z] //FullForm


Hold[LeftTee[LeftTee[x,y],z]]

does not parse to LeftTee[x, y, z]. In fact, syntax coloring gives a clue about this:

That is, LeftTee should only be used as a binary operator. On the other hand, CircleDot does parse to the Flat version:

Hold[x ⊙ y ⊙ z] //FullForm


Hold[CircleDot[x,y,z]]

• Is this the canonical way? I try to avoid setting these attributes unless they are specifically needed as they affect pattern matching in ways I don't always understand, or at least anticipate, e.g. (143836), (18060), (71494). Is it logical to conclude that one wants these pattern matching changes if simple left-associativity is desired? If so why doesn't e.g LeftTee have these by default? Oct 13, 2017 at 10:58
• Thanks, Carl. This does work. It is interesting that the attributes must be set before defining the operator. Setting its attributes after defining its functionality does not work. (I am also interested in Mr.Wizard's comment.) Oct 14, 2017 at 18:39

You can include your own associativity rule:

x_⊙y_ := x.y
x_⊙y_⊙z__ := (x⊙y)⊙z

z⊙y⊙x

{124, 170}


Or pick an operator that natively has the desired associativity:

x_ ⊣ y_ := x.y

z ⊣ y ⊣ x

{124, 170}