8

提前为初学者问题道歉,但我一直在努力寻找有用的信息。我正在研究“Learn You Haskell for Great Good”,并试图理解派生关键字,这似乎是 Java 的实现,但由于类别理论或其他原因,它应该具有很酷的自动代码生成。我为 2 向量声明了一个数据结构,例如

data R2 = R2 {x :: Double, y :: Double} deriving (Show)

然后我可以用它来做类似的事情

show (R2 1.0 2.0)

现在我想做的是向量加法和标量乘法,比如

(2.0 * (R2 1.0 2.0)) + (R2 3.0 4.0)

但是当我尝试

Prelude> data R2 = R2 { x :: Double, y :: Double} deriving (Num,Show)
     <interactive>:3:52:
     Can't make a derived instance of `Num R2':
         `Num' is not a derivable class
     In the data declaration for `R2'

所以编译器想出了如何显示原始类型的笛卡尔积,但是加法太难了?也许 Num 不是派生的正确类型类?多久可以期望派生一个类型类并在没有额外工作的情况下获得工作代码,例如我如何不必编写自己的 show 函数?

非常感谢,

约翰

4

5 回答 5

6

试图理解派生关键字,它看起来像 Java 的实现,但据说有很酷的自动代码生成

instance有点像implements,因为你声明一个类型是一个类型类的实例,然后编写实现。deriving都是关于这些实现的酷自动生成(尽管它确实包含instance)。

多久可以期望派生一个类型类并在没有额外工作的情况下获得工作代码,例如我如何不必编写自己的 show 函数?

Alexey Romanov 的回答涵盖了哪些课程deriving有效。还有另一种自动生成实例的方法:使用泛型。从鸟瞰的角度来看,它的工作原理是这样的:您描述一个泛型类型的实例应该是什么样子,然后,对于您想要拥有实例的任何类型,派生Generic并添加一个空的(即没有实现,因为它们将自动生成)instance声明。一些库喜欢aesonbinary 提供可供使用的通用实例,您当然可以为自己的类滚动自己的实例。

于 2015-08-10T09:32:09.003 回答
5

这是您无法派生 Num 类的原因之一

data Vector = Vector Int Int

instance Num Vector where
    Vector a b + Vector c d = Vector (a + c) (b + d)
    Vector a b * Vector c d = Vector (a * c) (b * d)

data Complex = Complex Int Int

instance Num Complex where
    Complex a b + Complex c d = Complex (a + c) (b + d)
    Complex a b * Complex c d = Complex (a * c - b * d) (a * d + b * c)

两者都是真正的程序员可能想要定义的合理实例。对于具有两个Int字段的给定数据定义,该deriving子句应选择哪个实例?

于 2015-08-10T09:26:26.767 回答
5

请参阅https://downloads.haskell.org/~ghc/7.10.2/docs/html/users_guide/deriving.html

在 Haskell 98 中,唯一可能出现在派生子句中的类是标准类 Eq、Ord、Enum、Ix、Bounded、Read 和 Show。

GHC 还允许派生GenericFunctorData、和for声明Typeable,以及声明的任何类(在启用相关扩展后,如链接页面中所列)。FoldableTraversabledatanewtype

于 2015-08-10T09:18:29.370 回答
4
于 2015-08-10T09:21:35.957 回答
0

可悲的是,派生只适用于少数类,其中必要的代码被硬连接到编译器中。您可以自己为任何类编写实例,但只有少数可以自动派生。

于 2015-08-10T12:17:41.520 回答