Выполнение алгебры с новыми типами на основе целых чисел Haskell - PullRequest
0 голосов
/ 07 ноября 2018

У меня проблемы с выполнением простого сложения, вычитания - любой тип алгебры с новым типом Haskells.

Мое определение (шоу включено, чтобы я мог распечатать их на консоли):

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

Я не могу найти четкого и краткого объяснения того, как наследование здесь работает. Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Вы упускаете пример того, как ваши деньги могут складываться, подсказка была в ошибке Instance : Num Money.

Итак, для добавления в Haskell типа Num необходимо добавить две вещи вместе, пока вы работаете с числами, поэтому давайте создадим экземпляр Num для Money:

newtype Money =
  Money Integer deriving Show

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

-- Money 1 + Money 2 == Money 3

Обратите внимание, что он возвращает Money, позволит вам выяснить, как вы можете получить число из типа:)

0 голосов
/ 07 ноября 2018

Ну, Хаскелл не может сложить два 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 (ну, конечно, они могут иметь эту функцию).

...