Haskell Language
Arithmétique
Recherche…
Introduction
Dans Haskell, toutes les expressions (qui incluent les constantes numériques et les fonctions fonctionnant sur celles-ci) ont un type décidable. Au moment de la compilation, le vérificateur de type déduit le type d'une expression à partir des types des fonctions élémentaires qui le composent. Les données étant immuables par défaut, il n'y a pas d'opérations de "type casting", mais certaines fonctions copient des données et généralisent ou spécialisent les types dans des limites raisonnables.
Remarques
La hiérarchie des classes de caractères numériques
Num
trouve à la racine de la hiérarchie numérique des classes de caractères. Ses opérations caractéristiques et certaines instances communes sont indiquées ci-dessous (celles chargées par défaut avec Prelude plus celles de 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’
Nous avons déjà vu la classe Fractional
, qui nécessite Num
et introduit les notions de "division" (/)
et de réciproque d'un nombre:
λ> :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’
Les modèles de classe Real
.. les vrais nombres. Il nécessite Num
et Ord
, il modélise donc un champ numérique ordonné. Comme contre-exemple, les nombres complexes ne sont pas un champ ordonné (c’est-à-dire qu’ils ne possèdent pas de relation d’ordre naturel):
λ> :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
représente des nombres qui peuvent être arrondis
λ> :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
(qui implique Fractional
) représente les constantes et les opérations qui ne peuvent pas avoir une expansion décimale finie.
λ> :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’
Attention: les expressions telles que sqrt . negate :: Floating a => a -> a
sont parfaitement valides, ils peuvent renvoyer NaN
("not-a-number"), ce qui peut ne pas être un comportement voulu. Dans de tels cas, nous pourrions vouloir travailler sur le champ complexe (montré plus tard).
Exemples de base
λ> :t 1
1 :: Num t => t
λ> :t pi
pi :: Floating a => a
Dans les exemples ci-dessus, le vérificateur de type déduit une classe de type plutôt qu'un type concret pour les deux constantes. Dans Haskell, la classe Num
est la classe numérique la plus générale (car elle englobe les entiers et les réels), mais pi
doit appartenir à une classe plus spécialisée, car elle comporte une partie non nulle.
list0 :: [Integer]
list0 = [1, 2, 3]
list1 :: [Double]
list1 = [1, 2, pi]
Les types de béton ci-dessus ont été déduits par GHC. Des types plus généraux tels que list0 :: Num a => [a]
auraient fonctionné, mais auraient également été plus difficiles à préserver (par exemple, si on avait consigné un Double
sur une liste de Num
s), en raison des réserves indiquées ci-dessus.
`Impossible de déduire (Fractional Int) ... '
Le message d'erreur dans le titre est une erreur courante des débutants. Voyons comment cela se passe et comment le réparer.
Supposons que nous devions calculer la valeur moyenne d'une liste de nombres; la déclaration suivante semblerait le faire, mais elle ne compilerait pas:
averageOfList ll = sum ll / length ll
Le problème est avec la fonction division (/)
: sa signature est (/) :: Fractional a => a -> a -> a
, mais dans le cas au-dessus du dénominateur (donné par length :: Foldable t => ta -> Int
) est de type Int
(et Int
n'appartient pas à la classe Fractional
) d'où le message d'erreur.
Nous pouvons corriger le message d'erreur avec fromIntegral :: (Num b, Integral a) => a -> b
. On peut voir que cette fonction accepte les valeurs de tout type Integral
et renvoie les valeurs correspondantes dans la classe Num
:
averageOfList' :: (Foldable t, Fractional a) => t a -> a
averageOfList' ll = sum ll / fromIntegral (length ll)
Exemples de fonction
Quel est le type de (+)
?
λ> :t (+)
(+) :: Num a => a -> a -> a
Quel est le type de sqrt
?
λ> :t sqrt
sqrt :: Floating a => a -> a
Quel est le type de sqrt . fromIntegral
?
sqrt . fromIntegral :: (Integral a, Floating c) => a -> c