2015-07-12 2 views
6

У меня есть трехмерный векторный тип данных, определенный как 3 поплавки. Я понимаю, что если я предоставляю экземпляр Num для своего класса и определяю нормальные математические операторы, я могу использовать их в своем классе.Зачем нужна сумма GHC.Num.fromInteger?

data Vec3 = Vec3 { x :: Float 
       , y :: Float 
       , z :: Float 
       } deriving (Show, Eq) 

instance Num Vec3 where 
    (+) v1 v2 = Vec3 (x v1 + x v2) (y v1 + y v2) (z v1 + z v2) 

Когда я загружаю свой файл в GHCI, я получаю предупреждение, потому что я не определить все функции в Num, что имеет смысл.

Prelude> :l temp.hs 
[1 of 1] Compiling Main    (temp.hs, interpreted) 

temp.hs:6:10: Warning: 
    No explicit method or default declaration for `*' 
    In the instance declaration for `Num Vec3' 

temp.hs:6:10: Warning: 
    No explicit method or default declaration for `abs' 
    In the instance declaration for `Num Vec3' 

temp.hs:6:10: Warning: 
    No explicit method or default declaration for `signum' 
    In the instance declaration for `Num Vec3' 

temp.hs:6:10: Warning: 
    No explicit method or default declaration for `fromInteger' 
    In the instance declaration for `Num Vec3' 
Ok, modules loaded: Main. 

Однако я все еще могу использовать те, которые я определил.

*Main> let a = Vec3 1.0 2.0 3.0 
*Main> let b = Vec3 2.0 4.0 5.0 
*Main> a + b 
Vec3 {x = 3.0, y = 6.0, z = 8.0} 

Моя путаница происходит из-за ошибки, я получаю при попытке использовать функцию суммы

*Main> sum [a,b] 
Vec3 {x = *** Exception: temp.hs:6:10-17: No instance nor default method for class operation GHC.Num.fromInteger 

Почему нужна сумма в fromInteger определение для моего типа данных Vec3? Во-первых, я бы предположил, что сумма использует только функцию +, а для другого мой тип данных не использует Integer.

+0

Вы должны всегда определять ** все ** операции в минимальном полном определении, прежде чем рассматривать тип данных как экземпляр класса ... – Bakuriu

+2

Настоящая проблема заключается в том, что вы создаете 'Vec3' экземпляр' Num', хотя векторы не являются числами. – rightfold

+0

Чтобы расширить на @rightfold - не делайте что-то экземпляр Num только потому, что вы хотите + - векторы не являются числами, даже если некоторые из операторов, которые вы используете на векторах, имеют то же имя, что и числовые операторы. (хотя вы _can_ рассматриваете векторы как числа, если по «вектору» вы просто означаете «набор чисел с фиксированным размером» вместо вектора в геометрическом или физическом смысле). – Cubic

ответ

9

Вот как реализуется сумма:

sum = foldl (+) 0 

Обратите внимание на 0 буквальным. Давайте проверим это типа в GHCi:

λ> :t 0 
0 :: Num a => a 

Как выясняется, числовые литералы сахар для fromInteger. Т.е., 0 фактически fromInteger 0.

Таким образом, sum требует fromInteger, потому что выше определение сахара для:

sum = foldl (+) (fromInteger 0) 

Реализация fromInteger легко:

instance Num Vec3 where 
    fromInteger n = let a = (fromInteger n) in Vec3 a a a 

Кроме того, я настоятельно рекомендую, всякий раз, когда вы делаете экземпляр, всегда определяйте его полностью, чтобы избежать непредвиденных неприятностей, подобных этому.

+1

Это имеет смысл. Тангенциальный вопрос, но где я могу найти реализации этих общих функций? Я попытался посмотреть, но не знал, где искать – user3288829

+0

@ user3288829 https://www.haskell.org/hoogle/?hoogle=sum – phadej

+0

@ user3288829 Исходный файл ['GHC.Num'] (https: // hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Num.html). – AJFarmar

4

Что должно sum [] :: Vec3 возвращение?


sum функция может быть определена как

sum :: (Num a) => [a] -> a 
sum = foldl (+) 0 

0 есть на самом деле fromInteger (0 :: Integer), поэтому вам нужно fromInteger использовать sum.

На самом деле в базе-4,8 sum определяется в терминах SumMonoid и Foldable, но это другая история. Вам все еще нужно fromInteger (0 :: Integer).

Смежные вопросы