简体   繁体   English

haskell 数据类型中的“继承”操作

[英]"Inheriting" operations in haskell data types

I have created the following type:我创建了以下类型:

data Inch = Inch Double
instance Show Inch where
    show (Inch i) = show i ++ " inches"

Now, I'd like to be able to perform some mathematical operations on this type and, since the type itself is basically just a synonym with Double I was expecting to get them for free.现在,我希望能够对这种类型执行一些数学运算,并且由于类型本身基本上只是Double的同义词,因此我希望免费获得它们。 However, this is not the case:然而,这种情况并非如此:

ghci> (3 :: Double) / 2
1.5
ghci> (3 :: Inch) / 2

<interactive>:74:2: error:
    • No instance for (Num Inch) arising from the literal ‘3’
    • In the first argument of ‘(/)’, namely ‘(3 :: Inch)’
      In the expression: (3 :: Inch) / 2
      In an equation for ‘it’: it = (3 :: Inch) / 2

<interactive>:74:13: error:
    • No instance for (Fractional Inch) arising from a use of ‘/’
    • In the expression: (3 :: Inch) / 2
      In an equation for ‘it’: it = (3 :: Inch) / 2

I think I can solve this by defining:我想我可以通过定义来解决这个问题:

(/) :: Inch -> Double -> Inch
(Inch i) / n = Inch (i GHC.Real./ n)

This allows the previous code to run fine:这允许前面的代码运行良好:

ghci> (Inch 3) / 2
1.5 inches

But I feel it's cumbersome and can't help thinking there surely must be a better way.但我觉得这很麻烦,不禁想到肯定有更好的方法。 Is there?在那儿?

I would recommend this:我会推荐这个:

{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}

import GHC.Generics
import Data.VectorSpace

data Length = Inches Double
  deriving (Generic, Show, AdditiveGroup, VectorSpace)

Then然后

ghci> Inches 3 ^/ 2
Inches 1.5

I'm using the ^/ operator from the vector-space package because that actually has the suitable type v -> Scalar v -> v .我正在使用向量空间 package 中的^/运算符,因为它实际上具有合适的类型v -> Scalar v -> v By contrast, the standard / from the Fractional class has simply type v -> v -> v , which would be Length -> Length -> Length in this case, which does not make sense physically.相比之下, 来自Fractional class 的标准/具有简单的类型v -> v -> v ,在这种情况下将是Length -> Length -> Length ,这在物理上没有意义。

You need to tell the compiler that Inch is a number by making it an instance of some suitable class.您需要通过使其成为一些合适的 class 的实例来告诉编译器Inch是一个数字。 One option is the Num class:一种选择是Num class:

> :info Num
class Num a where
  (+) :: a -> a -> a
  (-) :: a -> a -> a
  (*) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
  {-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}

Now we define Inch .现在我们定义Inch I'll use a newtype instead of data so we can use GeneralizedNewtypeDeriving to implement the Num instance more easily:我将使用新GeneralizedNewtypeDeriving而不是data ,以便我们可以使用newtype更轻松地实现Num实例:

> :set -XGeneralizedNewtypeDeriving
> newtype Inch = Inch Int deriving (Eq, Ord, Read, Show, Num)
> Inch 3 + Inch 5
Inch 8

It is worth noting that Inch is actually not a number - if you try Inch 4 / Inch 2 you get Inch 2 , but to be correct the units should cancel out, leaving a unitless 2 .值得注意的是 Inch 实际上不是一个数字——如果你尝试Inch 4 / Inch 2你会得到Inch 2 ,但是正确的单位应该取消,留下一个没有单位的2 But how to deal with that is beyond the scope of this answer, in which I pedantically uses more old fashioned constructs但是如何处理这个问题超出了这个答案的 scope ,我在其中迂腐地使用了更老式的结构

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM