I want to create a fairly simple abstraction layer over the top of Linear.Matrix so that I can later swap out the implementation for other libraries like hmatrix.
The abstraction layer is only going to support basic operations like matrix construction, addition, multiplication, inverse, and a few other functions.
As a relative newcomer to Haskell I'm a bit confused about how one implements a wrapper of an existing type.
For example, I started with:
import Linear (M44, V4 (V4))
type MyMatrix44 = M44 (Double)
type MyRow4 = V4 (Double)
My understanding of the type keyword is that it creates a type alias that is largely ignored by the compiler. However when I query in GHCi:
λ> :t V4
V4 :: a -> a -> a -> a -> V4 a
But:
λ> :t MyRow4
<interactive>:1:1: error: Data constructor not in scope: MyRow4
Yet:
λ> :i MyRow4
type MyRow4 = V4 Double -- Defined at <interactive>:10:1
So what is that telling me? That MyRow4 is not actually a type alias for V4 (Double)? Or does :t not actually show info for types unless they have a value constructor of the same name?
If I try and construct a value with my new type:
λ> MyRow4 1.0 2.0 3.0 4.0
<interactive>:15:1: error:
Data constructor not in scope:
MyRow4 :: Double -> Double -> Double -> Double -> t
So I need to wrap the value constructor too? I'm missing something fundamental here. Am I getting confused by the convention of type names often being the same name as value constructors?
I've read through Making Our Own Types and Typeclasses and I thought I understood the difference between types and value constructors, but I can't see how this translates into wrapping a type.
Where am I going wrong? Is there a good example of this kind of wrapper?
EDIT: rather than trying to alias the type, would I be better to create a wrapper that is composed of the underlying type, perhaps using a data structure?
Supplementary Questions
1: When a type is defined by:
data T = D1 Char | D2 Int
I understand that the "type constructor" is named T, but the terminology also talks about T being the "type". Are they one-and-the same, or two separate things that are (always?) called the same name?
2: In Linear.Matrix, this definition exists:
type M44 a = V4 (V4 a)
Does that mean that if I do :t v where v is a value, and get M44 Double that it’s the same type as if I get V4 (V4 Double) from :t with a different value? Are those values of the same type but the first is described using the type constructor, and the latter using the data constructor? I see this a lot with Linear.Matrix, where sometimes the type of the result is M44 a and other times it is V4 (V4 a), yet they seem to be the same thing.
3: "If you hide the data constructor from your interface (by not exporting it from the module in which you have defined it)" - if you name the data constructor the same thing as the type constructor, how does the export list differentiate between the two? A user of the module might want to pattern-match on the type, even if the data constructor is hidden, but omitting the name from the export list hides the type as well. One way to work around this is to use a different name for the data constructor, but that seems uncommon.