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}
Addendum
Addressing the comments.
I think this is the canonical way to create an associative operator.
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.
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]]