To quote LYAH: “Functors are things that can be mapped over”.
So when I tried to fmap over a matrix, I was surprised to discover that Matrix is not an instance.
m = (2><3)[3,5,4,6,5,7]
x = fmap (1+) m
-- "No instance for (Functor Matrix) arising from a use of ‘fmap’"
Ahh I think I understand… The constructor Matrix in the definition is hidden (so we can’t pattern match ). Instead members of Matrix class are produced by functions like matrix, (><), fromColumns, etc. - functions whose type definitions restrict the type of matrix elements to Double (type alias R), Element or Storable type. If Matrix could be a member of Functor then fmap :: (a -> b) -> Matrix a -> Matrix b would need to apply for arbitrary a and b. But it would only make sense for the type signature fmap :: (Storable a, Storable b) => (a -> b) -> Matrix a -> Matrix b. Essentially this is what cmap does, but where Container takes the role of Functor, and cmap takes the role of fmap. (Aside - it seems to me that this limited fmap-like functionality - call it SemiFunctor - should be totally generic, whereas the Container implementation limits what can be ‘contained’ to Elements. Has this been done? Would it be a worthwhile addition to the Haskell ecosystem?)
I wonder - if Matrix is to be limited to Storable (or in practice, Element) types, why not embed this in the data declaration using GADTs?
data Matrix t where
Matrix :: Storable t => Int -> Int -> Int -> Int -> (Vector t) -> Matrix t
-- irows icols xRow xCol xdat
The problem with embedding with GADTs is you now have no way of guaranteeing to the compiler that two Matrix t share the same Storable dictionary. Also, you don’t get any benefit out of it – it just makes things more complicated. Try coding up some simple examples in that style and you’ll see the limitations soon enough for yourself!