funciones - haskell main
Comportamiento contradictorio de las funciones de Lambda (2)
Usando las siguientes definiciones:
lenDigits n = length (show n)
factorial n = product [1..n]
Yo evalúo lo siguiente
Prelude> ((lenDigits . factorial) 199) <= 199
False
Prelude> (/i -> ((lenDigits . factorial) i) <= i) 199
True
¿Cuál es la razón de tal comportamiento? Según lo veo, la primera expresión es la misma que la segunda expresión con las lambdas reducidas.
Aquí hay una explicación paso a paso de esta pregunta.
Comencemos con:
((lenDigits . factorial) 199) <= 199
De acuerdo con el Informe Haskell ...
Un literal entero representa la aplicación de la función
fromInteger
al valor apropiado de tipoInteger
.
Eso significa que nuestra primera expresión es en realidad:
((lenDigits . factorial) (fromInteger (199 :: Integer))
<= (fromInteger (199 :: Integer))
Por sí mismo, fromInteger (199 :: Integer)
tiene el tipo polimórfico Num a => a
. Ahora tenemos que ver si este tipo está especializado en el contexto de la expresión completa. Tenga en cuenta que, hasta que encontremos una razón para que no sea así, deberíamos suponer que los tipos polimórficos de las dos ocurrencias de fromInteger (199 :: Integer)
son independientes ( Num a => a
y Num b => b
, si será).
lenDigits
es Show a => a -> Int
, y entonces el ...
(lenDigits . factorial) (fromInteger (199 :: Integer))
... a la izquierda del <=
debe ser un Int
. Dado que (<=)
es Ord a => a -> a -> Bool
, el fromInteger (199 :: Integer)
a la derecha de <=
también tiene que ser un Int
. La expresión completa se convierte en:
((lenDigits . factorial) (fromInteger (199 :: Integer)) <= (199 :: Int)
Mientras que el segundo 199
se especializó en Int
, el primero sigue siendo polimórfico. A falta de otras anotaciones de tipo, el valor predeterminado lo hace especializarse en Integer
cuando usamos la expresión en GHCi. Por lo tanto, finalmente obtenemos:
((lenDigits . factorial) (199 :: Integer)) <= (199 :: Int)
Ahora, a la segunda expresión:
(/i -> ((lenDigits . factorial) i) <= i) 199
Por el mismo razonamiento utilizado anteriormente, (lenDigits . factorial) i
(a la izquierda de <=
) es un Int
, y entonces i
(a la derecha de <=
) también es un Int
. Siendo así, tenemos ...
GHCi> :t /i -> (lenDigits . factorial) i <= i
/i -> (lenDigits . factorial) i <= i :: Int -> Bool
... y por lo tanto, aplicándolo a 199
(que en realidad es de fromInteger (199 :: Integer)
) lo especializa en int, dando:
((lenDigits . factorial) (199 :: Int)) <= (199 :: Int)
Los primeros 199
ahora son Int
lugar de Integer
. factorial (199 :: Int)
desborda el tipo de Int
tamaño fijo, lo que lleva a un resultado falso. Una forma de evitarlo sería introducir un contenido explícito de parte de un fromInteger
para obtener algo equivalente al primer escenario:
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
Porque en la primera expresión, el primer 199
tiene tipo Integer
y el segundo tiene valor Int
. Pero en la segunda expresión, ambos tienen el tipo Int
y el factorial 199
no se puede representar con el tipo Int
.