27 votes

Comportement contradictoire des fonctions Lambda

En utilisant les définitions suivantes:

 lenDigits n = length (show n)
factorial n = product [1..n]
 

J'évalue les éléments suivants

 Prelude> ((lenDigits . factorial) 199) <= 199
False
Prelude> (\i -> ((lenDigits . factorial) i) <= i) 199
True
 

Quelle est la raison d'un tel comportement? Selon moi, la première expression est la même que la deuxième expression avec les lambdas réduits.

26voto

freestyle Points 3182

Parce que dans la première expression, le premier 199 a le type Integer et le second a Int . Mais dans la deuxième expression, les deux types de type Int et factorial 199 ne peuvent pas être représentés par le type Int .

8voto

duplode Points 3803

Voici une étape par étape à prendre sur cette question.

Commençons par:

((lenDigits . factorial) 199) <= 199

Selon le Haskell Rapport...

Un littéral entier représente l'application de la fonction fromInteger de la valeur appropriée de type Integer.

Cela signifie que la première expression est en réalité:

((lenDigits . factorial) (fromInteger (199 :: Integer)) 
    <= (fromInteger (199 :: Integer))

Par lui-même, fromInteger (199 :: Integer) a le type polymorphe Num a => a. Nous avons maintenant à voir si ce type est spécialisée dans le contexte de l'ensemble de l'expression. Notez que, jusqu'à ce que nous trouver une raison pour ne pas l'être, on doit supposer que les types polymorphes de les deux occurrences de fromInteger (199 :: Integer) sont indépendants (Num a => a et Num b => b, si vous voulez).

lenDigits est Show a => a -> Int, et donc le...

(lenDigits . factorial) (fromInteger (199 :: Integer))

... à gauche de l' <= doit être un Int. Étant donné qu' (<=) est Ord a => a -> a -> Bool, l' fromInteger (199 :: Integer) à la droite de l' <= doit également être un Int. L'ensemble de l'expression devient alors:

((lenDigits . factorial) (fromInteger (199 :: Integer)) <= (199 :: Int)

Tandis que le second 199 était spécialisée à l' Int, le premier est toujours polymorphes. En l'absence d'autres annotations de type, par défaut le rend se spécialiser à l' Integer lorsque nous utilisons l'expression dans GHCi. Par conséquent, nous obtenons finalement:

((lenDigits . factorial) (199 :: Integer)) <= (199 :: Int)

Maintenant, à la seconde expression:

(\i -> ((lenDigits . factorial) i) <= i) 199

Par le même raisonnement que celui utilisé ci-dessus, (lenDigits . factorial) i (à la gauche de l' <=) est un Int, et ainsi de i (à droite de l' <=) est également un Int. Cela étant, nous avons...

GHCi> :t \i -> (lenDigits . factorial) i <= i
\i -> (lenDigits . factorial) i <= i :: Int -> Bool

... et, par conséquent, en l'appliquant à d' 199 (qui est en fait fromInteger (199 :: Integer)) se spécialise à int, donnant:

((lenDigits . factorial) (199 :: Int)) <= (199 :: Int)

La première 199 est désormais Int plutôt que d' Integer. factorial (199 :: Int) des dépassements de taille fixe Int type, conduisant à un résultat erroné. Une façon d'éviter que serait l'introduction explicite fromInteger afin d'obtenir quelque chose d'équivalent pour le premier scénario:

GHCi> :t \i -> (lenDigits . factorial) i <= fromInteger i
\i -> (lenDigits . factorial) i <= fromInteger i :: Integer -> Bool
GHCi> (\i -> (lenDigits . factorial) i <= fromInteger i) 199
False

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X