Haskell Language
Rekenkundig
Zoeken…
Invoering
In Haskell hebben alle uitdrukkingen (inclusief numerieke constanten en functies die daarop werken) een beslissend type. Tijdens het compileren leidt de typecontrole het type expressie af van de typen elementaire functies waaruit deze is samengesteld. Omdat gegevens standaard onveranderlijk zijn, zijn er geen "type casting" -bewerkingen, maar er zijn functies die gegevens kopiëren en de typen binnen redelijke grenzen generaliseren of specialiseren.
Opmerkingen
De numerieke typeklassehiërarchie
Num
bevindt zich aan de basis van de numerieke typeklassehiërarchie. De karakteristieke bewerkingen en enkele veel voorkomende exemplaren worden hieronder weergegeven (standaard geladen met Prelude plus die van Data.Complex
):
λ> :i Num
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
{-# MINIMAL (+), (*), abs, signum, fromInteger, (negate | (-)) #-}
-- Defined in ‘GHC.Num’
instance RealFloat a => Num (Complex a) -- Defined in ‘Data.Complex’
instance Num Word -- Defined in ‘GHC.Num’
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Int -- Defined in ‘GHC.Num’
instance Num Float -- Defined in ‘GHC.Float’
instance Num Double -- Defined in ‘GHC.Float’
We hebben de Fractional
klasse al gezien, waarvoor Num
vereist is en de noties van "deling" (/)
en wederkerig van een aantal introduceert:
λ> :i Fractional
class Num a => Fractional a where
(/) :: a -> a -> a
recip :: a -> a
fromRational :: Rational -> a
{-# MINIMAL fromRational, (recip | (/)) #-}
-- Defined in ‘GHC.Real’
instance RealFloat a => Fractional (Complex a) -- Defined in ‘Data.Complex’
instance Fractional Float -- Defined in ‘GHC.Float’
instance Fractional Double -- Defined in ‘GHC.Float’
De Real
klasse modellen ... de echte cijfers. Het vereist Num
en Ord
, daarom modelleert het een geordend numeriek veld. Als tegenvoorbeeld zijn complexe getallen geen geordend veld (dwz ze hebben geen natuurlijke ordeningsrelatie):
λ> :i Real
class (Num a, Ord a) => Real a where
toRational :: a -> Rational
{-# MINIMAL toRational #-}
-- Defined in ‘GHC.Real’
instance Real Word -- Defined in ‘GHC.Real’
instance Real Integer -- Defined in ‘GHC.Real’
instance Real Int -- Defined in ‘GHC.Real’
instance Real Float -- Defined in ‘GHC.Float’
instance Real Double -- Defined in ‘GHC.Float’
RealFrac
vertegenwoordigt getallen die kunnen worden afgerond
λ> :i RealFrac
class (Real a, Fractional a) => RealFrac a where
properFraction :: Integral b => a -> (b, a)
truncate :: Integral b => a -> b
round :: Integral b => a -> b
ceiling :: Integral b => a -> b
floor :: Integral b => a -> b
{-# MINIMAL properFraction #-}
-- Defined in ‘GHC.Real’
instance RealFrac Float -- Defined in ‘GHC.Float’
instance RealFrac Double -- Defined in ‘GHC.Float’
Floating
(wat neerkomt op Fractional
) vertegenwoordigt constanten en bewerkingen die mogelijk geen eindige decimale uitbreiding hebben.
λ> :i Floating
class Fractional a => Floating a where
pi :: a
exp :: a -> a
log :: a -> a
sqrt :: a -> a
(**) :: a -> a -> a
logBase :: a -> a -> a
sin :: a -> a
cos :: a -> a
tan :: a -> a
asin :: a -> a
acos :: a -> a
atan :: a -> a
sinh :: a -> a
cosh :: a -> a
tanh :: a -> a
asinh :: a -> a
acosh :: a -> a
atanh :: a -> a
GHC.Float.log1p :: a -> a
GHC.Float.expm1 :: a -> a
GHC.Float.log1pexp :: a -> a
GHC.Float.log1mexp :: a -> a
{-# MINIMAL pi, exp, log, sin, cos, asin, acos, atan, sinh, cosh,
asinh, acosh, atanh #-}
-- Defined in ‘GHC.Float’
instance RealFloat a => Floating (Complex a) -- Defined in ‘Data.Complex’
instance Floating Float -- Defined in ‘GHC.Float’
instance Floating Double -- Defined in ‘GHC.Float’
Let op: terwijl uitdrukkingen zoals sqrt . negate :: Floating a => a -> a
zijn volkomen geldig, ze kunnen NaN
("not-a-number") retourneren, wat misschien niet de bedoeling is. In dergelijke gevallen willen we misschien over het complexe veld werken (later getoond).
Basis voorbeelden
λ> :t 1
1 :: Num t => t
λ> :t pi
pi :: Floating a => a
In de bovenstaande voorbeelden leidt de typecontrole een typeklasse in plaats van een concreet type voor de twee constanten. In Haskell is de klasse Num
de meest algemene numerieke (omdat deze gehele getallen en reals omvat), maar pi
moet tot een meer gespecialiseerde klasse behoren, omdat deze een niet-nul fractioneel deel heeft.
list0 :: [Integer]
list0 = [1, 2, 3]
list1 :: [Double]
list1 = [1, 2, pi]
De betonsoorten hierboven zijn afgeleid door GHC. Meer algemene typen zoals list0 :: Num a => [a]
zouden hebben gewerkt, maar zou ook moeilijker te bewaren zijn geweest (bijv. Als iemand een Double
op een lijst met Num
's had geplaatst), vanwege de hierboven weergegeven kanttekeningen.
"Kon niet afleiden (Fractional Int) ..."
De foutmelding in de titel is een veel voorkomende beginnersfout. Laten we kijken hoe het ontstaat en hoe het te repareren.
Stel dat we de gemiddelde waarde van een lijst met getallen moeten berekenen; de volgende verklaring lijkt het te doen, maar het zou niet compileren:
averageOfList ll = sum ll / length ll
Het probleem is met de delingsfunctie (/)
: de handtekening is (/) :: Fractional a => a -> a -> a
, maar in het geval boven de noemer (gegeven door length :: Foldable t => ta -> Int
) is van het type Int
(en Int
behoort niet tot de Fractional
klasse) vandaar de foutmelding.
We kunnen het foutbericht oplossen met fromIntegral :: (Num b, Integral a) => a -> b
. Men kan zien dat deze functie waarden van elk type Integral
accepteert en overeenkomstige waarden in de klasse Num
retourneert:
averageOfList' :: (Foldable t, Fractional a) => t a -> a
averageOfList' ll = sum ll / fromIntegral (length ll)
Functie voorbeelden
Wat is het type (+)
?
λ> :t (+)
(+) :: Num a => a -> a -> a
Wat is het type sqrt
?
λ> :t sqrt
sqrt :: Floating a => a -> a
Wat is het type sqrt . fromIntegral
?
sqrt . fromIntegral :: (Integral a, Floating c) => a -> c