         return FreeModuleHom(domainBasis: f.domainBasis, codomainBasis: f.codomainBasis, matrix: f.matrix * r)
     }
     
+    public static func ⊗<B: FreeModuleBase>(f: FreeModuleHom<A, R>, g: FreeModuleHom<B, R>) -> FreeModuleHom<Tensor<A, B>, R> {
+        let (k, l) = (g.codomainBasis.count, g.domainBasis.count)
+        
+        let domain = f.domainBasis.pairs(with: g.domainBasis).map{ (a, b) in a⊗b }
+        let codomain = f.codomainBasis.pairs(with: g.codomainBasis).map{ (a, b) in a⊗b }
+        let matrix = DynamicMatrix<R>(rows: codomain.count, cols: domain.count) { (i, j) -> R in
+            f.matrix[i / k, j / l] * g.matrix[i % k, j % l]
+        }
+        return FreeModuleHom<Tensor<A, B>, R>(domainBasis: domain, codomainBasis: codomain, matrix: matrix)
+    }
+    
     public var hashValue: Int {
         return 0 // TODO
     }

            }
        }
        return M(basis: codomainBasis, components: comps)
    }
    
    public static func ==(lhs: FreeModuleHom<A, R>, rhs: FreeModuleHom<A, R>) -> Bool {

        self.matrix = matrix.asDynamic
    }
    
    // TODO very slow
    public func appliedTo(_ m: M) -> M {
        let comps = (0 ..< codomainBasis.count).map{ i -> R in
