使用基于整数的新类型执行代数 Haskell

Performing algebra with newtypes based on integers Haskell

我在执行简单的加法、减法时遇到了一些麻烦 -- 任何类型的 Haskells newtype 代数。

我的定义是(包含显示以便我可以将它们打印到控制台):

newtype Money = Money Integer deriving Show

我想做的基本上是:

Money 15 + Money 5 = Money 20
Money 15 - Money 5 = Money 10
Money 15 / Money 5 = Money 3

依此类推,但我得到

m = Money 15
n = Money 5
Main>> m-n

ERROR - Cannot infer instance
*** Instance   : Num Money
*** Expression : m - n

对于这里的继承是如何工作的,我找不到一个清晰而简洁的解释。任何帮助将不胜感激。

好吧 Haskell 不能将两个 Money 相加,因为您从未指定如何做。为了将两个 a 相加,a 应该实现 Num 类型类。事实上newtype经常被用来指定不同的类型实例,例如Sum and Product被用来定义两个不同的幺半群。

因此您需要使其成为 Num 的实例,因此您必须定义一个实例,如:

instance Num Money where
    Money a + Money b = Money (a+b)
    Money a - Money b = Money (a-b)
    Money a * Money b = Money (a*b)
    abs (Money a) = Money (abs a)
    signum (Money a) = Money (signum a)
    fromInteger = Money

由于 (/) :: Fractional a => a -> a -> aFractional 类型类的成员,这会带来一些问题,因为您的 Money 包装了一个 Integer 对象。

但是您可以实现 Integral typeclass such that it supports div. In order to do this, we however need to implement the Real and Enum typeclass. The Real typeclass requires the type to be implement the Ord, and since the Ord typeclass requires the object to be an instance of the Eq 类型类,因此我们最终实现了 EqOrdRealEnum 类型类。

instance Eq Money where
    Money x == Money y = x == y

instance Ord Money where
    compare (Money x) (Money y) = compare x y

instance Real Money where
    toRational (Money x) = toRational x

instance Enum Money where
    fromEnum (Money x) = fromEnum x
    toEnum = Money . toEnum

instance Integral Money where
    toInteger (Money x) = x
    quotRem (Money x) (Money y) = (Money q, Money r)
        where (q, r) = quotRem x y

GeneralizedNewtypeDeriving

we can use a GHC extension named -XGeneralizedNewtypeDeriving.

上面的推导相当 "boring" 这里我们每次 "unwrap" 数据构造函数,执行一些操作,然后 "rewrap" 它们(在某些情况下要么解包要么不需要重新包装)。特别是因为 newtype 在运行时实际上并不存在(这更像是一种让 Haskell 区别对待数据的方式,但数据构造函数将是 "optimized away"),这没有多大意义.

如果我们编译:

ghc <b>-XGeneralizedNewtypeDeriving</b> file.hs

我们可以将 Money 类型声明为:

newtype Money = Money Integer deriving (Show, <b>Num</b>, Enum, Eq, Ord, <b>Real, Integral</b>)

和Haskell将为我们进行上述推导。据我所知,这是 GHC 的一项功能,因此其他 Haskell 编译器本身 (他们当然可以拥有此功能)不支持此功能。

您错过了如何将您的钱加在一起的实例,线索在错误 Instance : Num Money

因此,对于 Haskell 中的加法,Num 键入需要将两件事加在一起的内容,只要您处理的是数字,那么让我们在 [= 上创建 Num 的实例14=]:

newtype Money =
  Money Integer deriving Show

instance Num Money where
  Money a + Money b = Money $ a + b

-- Money 1 + Money 2 == Money 3

请注意,它 returns Money 会让您研究如何从类型中获取数字 :)