I\'m starting down the road of defining my own operators for Cartesian products and matrix multiplication.
With matrices and vectors aliased as lists:
The idiomatic solution is to define Matrix as a new type and add operators as static members:
type Matrix =
| MatrixData of float list list
static member (*) (MatrixData m1, MatrixData m2) = (...)
Operators defined using let are useful when you know that the definition does not conflict with anything else, or when you're defining the operator locally in a small scope (within a function).
For custom types, it is a good idea to define operators as static members. Another benefit of this approach is that your type will be usable from C# (including the operators).
To do this in your example, I had to change the type definition from type alias (which is not a stand-alone type and so it cannot have its own static members) to a single-case discriminated union, which can have members - but this is probably a good idea anyway.