На правах дилетанта позволите уточнить пару вопросов?
1) Вы предусматриваете каким-либо образом на этапе компиляции контроль одинаковых размерностей матриц при операциях с ними? Без создания своих типов для каждой размерности.
2) Если нет, то почему бы не выбрать в качестве нейтрального элемента просто бесконечный по всем измерениям тип, который участвует в операциях с элементами того же типа любых размерностей? С двумерными матрицами сложнее, возьмем одномерные вектора нефиксированной длины. Группу по сложению не стал рисовать, но на примере моноида идея видна:
import Data.Monoid
newtype Vec a = Vec [a] deriving Show
instance Monoid a => Monoid (Vec a) where
mempty = Vec $ repeat mempty
Vec a `mappend` Vec b = Vec $ zipWith mappend a b
main = print $ Vec (map Sum [1..3]) <> Vec (map Sum [10..])