2015-07-12 17 views
6

Ich habe einen 3D-Vektor-Datentyp definiert als 3 Floats. Ich verstehe, dass, wenn ich eine Num Instanz für meine Klasse zur Verfügung stelle und die normalen mathematischen Operatoren definiere, kann ich sie für meine Klasse verwenden.Warum benötigt sum 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) 

Wenn ich meine Datei in GHCI laden, bekomme ich Warnungen, weil ich nicht alle Funktionen in Num hat definieren, was Sinn macht.

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. 

Allerdings kann ich immer noch diejenigen verwenden, die ich definiert habe.

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

Meine Verwirrung kommt aus dem folgenden Fehler erhalte ich, wenn die Summenfunktion

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

zu verwenden versuchen Warum Summe eine fromInteger Definition für meinen Vec3 Datentyp benötigt? Zum einen hätte ich gedacht, dass die Summe nur die + Funktion verwendet, und zum anderen verwendet mein Datentyp Integer nicht.

+0

Sie sollten immer ** all ** die Operationen in der minimalen vollständigen Definition definieren, bevor Sie den Datentyp als Instanz einer Klasse betrachten ... – Bakuriu

+2

Das eigentliche Problem hier ist, dass Sie 'Vec3' eine Instanz von' Num' machen, obwohl Vektoren keine Zahlen sind. – rightfold

+0

Erweitern Sie @Rightfold - machen Sie keine Instanz von Num, nur weil Sie + wollen - Vektoren sind keine Zahlen, auch wenn einige der Operatoren, die Sie auf Vektoren verwenden, den gleichen Namen haben wie Zahlenoperatoren. (obwohl man Vektoren als Zahlen behandeln kann, wenn man mit 'Vektor' nur 'feste Größenansammlung von Zahlen' anstelle von Vektor in einem geometrischen oder physikalischen Sinn meint). – Cubic

Antwort

9

Hier ist, wie Summe implementiert:

sum = foldl (+) 0 

Beachten Sie die 0 wörtlich zu nehmen. Lassen Sie uns es in GHCi Typ:

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

Wie sich herausstellt, Zahlenliterale sind Zucker für fromInteger. Ie, 0 ist eigentlich fromInteger 0. So

erfordert sumfromInteger, weil die obige Definition ist Zucker für:

sum = foldl (+) (fromInteger 0) 

Die Implementierung von fromInteger ist einfach:

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

Außerdem würde ich sehr empfehlen, dass, Wenn Sie eine Instanz erstellen, definieren Sie sie immer vollständig, um unvorhergesehene Probleme wie diese zu vermeiden.

+1

Das macht Sinn. Tangentiale Frage, aber wo finde ich die Implementierungen dieser gemeinsamen Funktionen? Ich habe versucht zu suchen, aber wusste nicht, wo ich suchen sollte – user3288829

+0

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

+0

@ user3288829 Die Quelldatei ist ['GHC.Num'] (https: // hackage.haskell.org/package/base-4.7.0.2/docs/src/GHC-Num.html). – AJFarmar

4

Was sollte sum [] :: Vec3 zurückgeben?


Die sum Funktion könnte als

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

Die 0 dort definiert werden, ist eigentlich fromInteger (0 :: Integer), so müssen Sie fromIntegersum verwenden.

Eigentlich in basen 4.8 sum ist in Bezug auf SumMonoid und Foldable definiert, aber das ist anders. Sie brauchen noch fromInteger (0 :: Integer) dort.