2015-07-12 11 views
6

Mam 3d typ danych wektorowych zdefiniowany jako 3 pływaki. Rozumiem, że jeśli udostępnię instancję Num dla mojej klasy i zdefiniuję normalne operatory matematyczne, będę mógł użyć ich na mojej klasie.dlaczego suma potrzebuje 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) 

Kiedy załadować mój plik do ghci, dostaję ostrzeżenia, bo nie określają wszystkich funkcji Num, które ma sens.

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. 

Jednak nadal mogę używać tych, które zdefiniowałem.

*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} 

Moja dezorientacja pochodzi z następującym błąd pojawia się podczas próby użycia funkcji sumy

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

Dlaczego suma potrzebują fromInteger definicję dla mojego Vec3 typu danych? Po pierwsze, doszedłem do wniosku, że suma korzysta tylko z funkcji +, a po drugie, mój typ danych nie używa Integer.

+0

Powinieneś zawsze zdefiniować ** wszystkie ** operacji w minimalnej kompletnej definicji, zanim uznasz typ danych za instancję klasy ... – Bakuriu

+2

Prawdziwy problem polega na tym, że tworzysz 'Vec3' instancję' Num', chociaż wektory nie są liczbami. – rightfold

+0

Aby rozwinąć na @rightfold - nie rób czegoś z Num tylko dlatego, że chcesz + - wektory nie są liczbami, nawet jeśli niektóre z operatorów, których używasz na wektorach, mają zwykle taką samą nazwę jak operatory liczb. (chociaż możesz traktować wektory jako liczby, jeśli przez "wektor" oznacza się po prostu "zbiór o stałej wielkości liczb" zamiast wektora w sensie geometrycznym lub fizycznym). – Cubic

Odpowiedz

9

Oto jak suma jest realizowany:

sum = foldl (+) 0 

Zawiadomienie 0 dosłowne. Zobaczmy to wpisać GHCi:

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

Jak się okazuje, literały liczbowe są cukier dla fromInteger. Tj. 0 jest w rzeczywistości fromInteger 0.

Zatem sum wymaga fromInteger, ponieważ powyższa definicja jest cukier do:

sum = foldl (+) (fromInteger 0) 

Realizacja fromInteger jest proste:

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

Ponadto, gorąco polecam, że za każdym razem, gdy tworzysz instancję, zawsze definiuj ją całkowicie, aby uniknąć nieprzewidzianych takich problemów.

+1

To ma sens. Pytanie styczne, ale gdzie mogę znaleźć implementacje tych wspólnych funkcji? Próbowałem szukać, ale nie wiedziałem, gdzie szukać – user3288829

+0

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

+0

@ user3288829 Plik źródłowy to ['GHC.Num'] (https: // hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Num.html). – AJFarmar

4

Co powinienem zwrócić sum [] :: Vec3?


Funkcja sum można zdefiniować jako

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

0 istnieje rzeczywiście fromInteger (0 :: Integer), więc trzeba fromInteger używać sum.

Właściwie w base-4,8 sum jest definiowana w kategoriach SumMonoid i Foldable, ale to już inna historia. Nadal potrzebujesz fromInteger (0 :: Integer).

Powiązane problemy