Haskell Matrix似乎没有调用Num实例

时间:2016-01-12 00:02:28

标签: haskell

我正在尝试将2x2矩阵乘以学习练习。我正在尝试将Matrix类型设为Num的实例,以便我可以与*相乘。当我尝试在GHCI中执行(Matrix 1 2 3 4) *(Matrix 1 1 1 1)时,即使我使用灵活上下文,我也会收到错误,即使用FlexibleContexts to permit this

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
import Debug.Trace
data Matrix a b c d= Matrix a b c d
    deriving (Show)

instance Num a=>Num (Matrix a a a a) where
    (*) (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) =  (trace "* ran")(Matrix (a1*a2 + b1*c2) (a1*b2 + b1*d2) (c1*a2 + d1*c2) (c1*b2+d2*d1))

multMat (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) =  (trace "Multmat ran")(Matrix (a1*a2 + b1*c2) (a1*b2 + b1*d2) (c1*a2 + d1*c2) (c1*b2 + d1*d2))

我不认为我的*方法被调用,因为"* ran"从未打印出来。另一方面,如果我运行multMat(代码是相同的,除了函数名),调试语句就打印出来,我得到了预期的答案。

*我认为问题是当在Matrix数据类型上使用*时,会调用其他一些方法。我如何让它调用我的方法? *

在编译此代码时,它会在尝试使用*类型Matrix时出现运行时错误。

1 个答案:

答案 0 :(得分:3)

矩阵乘法的定义很好(虽然在语义上它是不正确的)。您的代码编译得很好。但问题是,如果您执行查询:

*Main> (Matrix 1 2 3 4) * (Matrix 7 5 3 1)

Haskell感到困惑的是它应该附加到12等哪种类型。它可以是IntIntegerFloat,{{1所有这些都有不同的实现(如果它选择Double,它将执行浮点乘法,这与整数乘法不同)。

解决方案是你给Haskell一个提示你正在使用的类型:

Float

毕竟,*Main> (Matrix (1 :: Int) (2 :: Int) (3 :: Int) (4 :: Int)) * (Matrix (7 :: Int) (5 :: Int) (3 :: Int) (1 :: Int)) Matrix 7 6 15 4 可以是任何类型的1类型(甚至是矩阵),内部Haskell会调用Num将其转换为正确的fromInteger类型。

这也是为什么从Num实例化Matrix并不简单的原因:您需要将整数转换为矩阵。由于您的Num数据类型只允许2 x 2矩阵,因此没有直接的方法。

最后要注意的是你的矩阵乘法是错误的。你应该使用:

Matrix

虽然我能理解这只是一个简单的测试。

附加说明

In the comments,@ stites问了一个有趣的问题,即仅仅实例化一种Matrix类型就足够了。类似的东西:

instance Num a=>Num (Matrix a a a a) where
    (*) (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) = Matrix (a1*a2+b1*c2) (a1*b2+b1*d2) (c1*a2+d1*c2) (b2*c1+d1*d2)
然而,Haskell对此有误,这是合理的。毕竟,一个有效的推理方法是,您仍然可以使用:

(Matrix (1 :: Int) 2 3 4) * (Matrix 7 5 3 1)

现在您只为Matrix Int Float Float Int 定义了instance,但可能会定义一个,例如:

Matrix a a a a

由于编译器应该保守,它不能简单地假设选择了最受欢迎的instance Num (Matrix Int Float Float Int) where (*) ... :也许你的印象是你定义了另一个实例某处。

此外,当您完全指定左矩阵的类型时,它可以工作:

instance

这是因为现在您已完全指定左操作数属于*Main> (Matrix (1 :: Int) (2 :: Int) (3 :: Int) (4 :: Int)) * (Matrix 7 5 3 1) Matrix 7 6 15 4 类型。由于Matrix Int Int Int Int具有签名:

(*)

(使用(*) :: Num e => e -> e -> e 来减少混淆),它知道e所以它可以"崩溃"右操作数:它因此可以导出右操作数,类型为e = Matrix Int Int Int Int

您甚至可以在任意一侧指定类型,例如:

Matrix Int Int Int Int

也可以。因为第一个Haskell派生出*Main> (Matrix (1 :: Int) 2 (3 :: Int) 4) * (Matrix 7 (5 :: Int) 3 (1 :: Int)) Matrix 7 6 15 4 。现在第一个操作数填入e = Matrix a b c d,第二个a = Int等等。

@ user2407038还提供了有用的comment。您也可以将您的定义写为:

b = Int

由于签名显示{-# LANGUAGE GADTs #-} -- ... instance (Num a, a ~ b, b ~ c, c ~ d) => Num (Matrix a b c d) where (*) (Matrix a1 b1 c1 d1) (Matrix a2 b2 c2 d2) = Matrix (a1*a2+b1*c2) (a1*b2+b1*d2) (c1*a2+d1*c2) (b2*c1+d1*d2) ,Haskell将尝试将乘法与此实例匹配。如果Matrix a b c d,它可以这样做。这是真的。