Ну, Хаскелл не может сложить два Money
с, так как вы никогда не указывали, как это сделать. Чтобы сложить два a
s, a
s должны реализовать класс типов Num
. Фактически newtype
s часто используются для указания различных экземпляров типа , например, Sum
и 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 -> a
является членом класса типов Fractional
, это создаст некоторые проблемы, поскольку ваш Money
оборачивает объект Integer
.
Однако вы можете реализовать класс типов Integral
так, чтобы он поддерживал div
. Однако для этого нам нужно реализовать класс типов Real
и Enum
. Класс типов Real
требует, чтобы тип реализовывал Ord
, а поскольку класс типов Ord
требует, чтобы объект был экземпляром класса типов Eq
, мы таким образом, в итоге реализуется класс типов Eq
, Ord
, Real
и Enum
.
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
Как сказал @ Алек , мы можем использовать расширение GHC с именем -XGeneralizedNewtypeDeriving
.
Вышеприведенные деривации довольно «скучны», здесь мы каждый раз «разворачиваем» конструктор (и) данных, выполняем некоторые действия и «перезаписываем» их (ну, в некоторых случаях распаковка или переупаковка не нужны). Тем более что newtype
фактически не существует во время выполнения (это больше способ позволить Haskell обрабатывать данные по-разному, но конструктор данных будет «оптимизирован»), это не имеет особого смысла.
Если мы скомпилируем с:
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 не поддерживают per se (ну, конечно, они могут иметь эту функцию).