2010-03-22 7 views
6

Después de jugar un poco con Haskell un poco tropecé con esta función:¿El sistema de tipos de Haskell trata un valor numérico como función?

Prelude Data.Maclaurin> :t ((+) . ($) . (+)) 
((+) . ($) . (+)) :: (Num a) => a -> (a -> a) -> a -> a 

(Data.Maclaurin se exporta por el paquete de espacio vectorial.) Por lo tanto se necesita un Num, una función, y en última instancia otra Num retornos a Num. ¿Qué magia hace que el siguiente trabajo?

Prelude Data.Maclaurin> ((+) . ($) . (+)) 1 2 3 
6 

2 obviamente no es una función (a-> a) o me perdí de algo?

+0

http://www.haskell.org/haskellwiki/Num_instance_for_functions – sdcvvc

Respuesta

16

El módulo de Data.NumInstances del mismo paquete defines una instancia Num para funciones que devuelven números:

instance Num b => Num (a->b) where 
    (+)   = liftA2 (+) 
    (*)   = liftA2 (*) 
    fromInteger = pure . fromInteger 
    ... 

En Haskell un número entero literal como 2 es genérico para que pueda representar un número para cualquier instancia de Num:

Prelude> :t 2 
2 :: (Num t) => t 

para convertirlo en un número real del tipo requerido en un contexto específico, fromInteger de laclase se llama.

Puesto que el módulo de ayuda mencionado anteriormente define una instancia de Num para funciones, 2 ahora se puede convertir a una función con el método fromInteger especificado allí. Entonces ghci llama a fromInteger 2 para obtener la función requerida como el segundo parámetro de la construcción en la pregunta. La expresión completa pasa a evaluar a 6.

+0

Además, * todos los funtores * aplicativas admiten estos casos para las clases numéricos . Consulte el paquete [números de aplicación] (http://hackage.haskell.org/package/applicative-numbers) para una manera fácil de definir tales instancias para cualquier funcionador de aplicación que desee. – Conal

1

Tiene buenas razones para sentirse confundido. Usando el módulo Data.NumInstances en GHC (que está cargado por Data.Maclaurin) es posible forzar un Num a una función constante.

Prelude Data.NumInstances> :t (2 :: (Num a) => a -> a) 
(2 :: (Num a) => a -> a) :: (Num a) => a -> a 
Prelude Data.NumInstances> (2 :: (Num a) => a -> a) 0   
2 
Prelude Data.NumInstances> (2 :: (Num a) => a -> a) 1000 
2 

La evaluación de la expresión es, esencialmente,

((+) . ($) . (+)) 1 2 3 = ((+) . ($) . (1+)) 2 3 
         = ((+) (1+)) 2 3 
         -- (+) is defined for functions that return a Num 
         = ((+) (1+) (\_ -> 2)) 3 
         = ((+2) . (1+)) 3 
         = 6 
+2

No, no es posible, a menos que tenga cargados algunos módulos no estándar que definen una instancia 'Num' para' a -> a'. –

+1

Bueno, maldición, podría jurar que funcionó sin Data.Maclaurin cargado, pero no es así. Como @sth señala, esta magia está habilitada por Data.NumInstances. –

Cuestiones relacionadas