Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>If you inspect the type of your <code>add</code> and <code>sub</code>, you will see the issue.</p> <pre><code>ghci&gt; :t add add :: Num a =&gt; [[a]] -&gt; [[a]] -&gt; [[a]] ghci&gt; :t sub sub :: Num a =&gt; [[a]] -&gt; [[a]] -&gt; [[a]] </code></pre> <p>Mikhail's suggestion was to essentially unwrap the 2D list and rewrap it in the Num instance methods. Another way to do this is to modify your <code>add</code> and <code>sub</code> methods to work on Matrices instead. Here I use a "lifting" approach, where I write combinators to "lift" a function from one type to another.</p> <pre><code>-- unwraps the 2d list from a matrix unMatrix :: Matrix a -&gt; [[a]] unMatrix (Matrix m) = m -- lifts a 2d list operation to be a Matrix operation liftMatrixOp :: ([[a]] -&gt; [[a]] -&gt; [[a]]) -&gt; Matrix a -&gt; Matrix a -&gt; Matrix a liftMatrixOp f x y = Matrix $ f (unMatrix x) (unMatrix y) -- lifts a regular operation to be a 2d list operation lift2dOp :: (a -&gt; a -&gt; a) -&gt; [[a]] -&gt; [[a]] -&gt; [[a]] lift2dOp f = zipWith (zipWith f) </code></pre> <p>With these combinators, defining <code>add</code> and <code>sub</code> is simply a matter of lifting appropriately.</p> <pre><code>add, sub :: Num a =&gt; Matrix a -&gt; Matrix a -&gt; Matrix a add = liftMatrixOp add2D sub = liftMatrixOp sub2D add2D, sub2D :: Num a =&gt; [[a]] -&gt; [[a]] -&gt; [[a]] add2D = lift2dOp (+) sub2D = lift2dOp (-) </code></pre> <p>Now that we have functions that work on Matrices, the Num instance is simple</p> <pre><code>instance (Num a) =&gt; Num (Matrix a) where (+) = add (-) = sub ..etc.. </code></pre> <p>Of course we could have combined <code>lift2dOp</code> and <code>liftMatrixOp</code> into one convenience function:</p> <pre><code>-- lifts a regular operation to be a Matrix operation liftMatrixOp' :: (a -&gt; a -&gt; a) -&gt; Matrix a -&gt; Matrix a -&gt; Matrix a liftMatrixOp' = liftMatrixOp . lift2dOp instance (Num a) =&gt; Num (Matrix a) where (+) = liftMatrixOp' (+) (-) = liftMatrixOp' (-) (*) = liftMatrixOp' (*) ..etc.. </code></pre> <p>Now you try: define <code>liftMatrix :: (a -&gt; a) -&gt; Matrix a -&gt; Matrix a</code>, a lifting function for unary functions. Now use that to define <code>negate</code>, <code>abs</code>, and <code>signum</code>. The docs suggest that <code>abs x * signum x</code> should always be equivalent to <code>x</code>. See if this is true for our implementation.</p> <pre><code>ghci&gt; quickCheck (\xs -&gt; let m = Matrix xs in abs m * signum m == m) +++ OK, passed 100 tests. </code></pre> <p>In fact, if you write <code>liftMatrix</code> with the more lenient type signature, it can be used to define a <code>Functor</code> instance for Matrices.</p> <pre><code>liftMatrix :: (a -&gt; b) -&gt; Matrix a -&gt; Matrix b instance Functor (Matrix a) where fmap = liftMatrix </code></pre> <p>Now think about how you could implement <code>fromInteger</code>. Implementing this allows you to do stuff like this in ghci:</p> <pre><code>ghci&gt; Matrix [[1,2],[3,4]] + 1 Matrix [[2,3],[4,5]] </code></pre> <p>That's how it works the way I implemented it, anyways. Remember that any numeric literal <code>n</code> in Haskell code is actually transformed into <code>fromInteger n</code>, which is why this works.</p> <p>I think that's enough fun for now, but if you need more exercises, try getting comfortable with this <code>Arbitrary</code> instance of Matrices:</p> <pre><code>instance Arbitrary a =&gt; Arbitrary (Matrix a) where arbitrary = liftM Matrix arbitrary </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload