haskell - ¿Polimorfismo dentro de las funciones de orden superior?
types polymorphism (1)
Sí, esto es posible, pero solo con extensiones de idioma :
{-# LANGUAGE Rank2Types #-}
helper :: (forall a. (Eq a) => (a -> a -> Bool))
-> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing
El forall a.
hace sobre cómo suena; la a
está universalmente cuantificada dentro de los paréntesis, y fuera de alcance fuera de ellos. Esto significa que se requiere que el argumento f
sea polimórfico sobre todos los tipos a que son instancias de Eq
, que es exactamente lo que desea.
La extensión aquí se llama "rango 2" porque permite el estilo regular de polimorfismo en el ámbito más externo, más los argumentos polimórficos como en el ejemplo aquí. Para anidar más las cosas, necesitas la extensión RankNTypes
, que es bastante autodescriptiva.
Como nota aparte, con respecto a los tipos polimórficos de rango más alto, tenga en cuenta que el forall
es lo que realmente une la variable a un tipo; puedes pensar que se comportan mucho como un lambda, de hecho. Cuando aplica una función de este tipo a algo con un tipo concreto, el tipo del argumento está vinculado implícitamente por el forall
para ese uso. Esto surge, por ejemplo, si intenta usar un valor cuyo tipo estaba vinculado por un forall
interno fuera de esa función; El tipo de valor ha quedado fuera del alcance, lo que dificulta hacer algo sensato (como probablemente pueda imaginar).
Tengo un tipo de datos algebraico con algunos constructores que tienen valores comparables, y algunos constructores que no lo hacen. Escribí algunas funciones de comparación que funcionan como los operadores estándar (==)
y (/=)
, pero no devuelven Nothing
para las comparaciones que no tienen sentido:
data Variant = IntValue Int
| FloatValue Float
| NoValue
equal :: Variant -> Variant -> Maybe Bool
equal (IntValue a) (IntValue b) = Just (a == b)
equal (FloatValue a) (FloatValue b) = Just (a == b)
equal _ _ = Nothing
unequal :: Variant -> Variant -> Maybe Bool
unequal (IntValue a) (IntValue b) = Just (a /= b)
unequal (FloatValue a) (FloatValue b) = Just (a /= b)
unequal _ _ = Nothing
Eso funciona, pero la repetición es difícil de manejar, especialmente porque en realidad tengo más constructores de Variant
y más funciones de comparación.
Pensé que podría factorizar la repetición en una función auxiliar que está parametrizada en la función de comparación:
helper :: (Eq a) => (a -> a -> Bool) -> Variant -> Variant -> Maybe Bool
helper f (IntValue a) (IntValue b) = Just (f a b)
helper f (FloatValue a) (FloatValue b) = Just (f a b)
helper _ _ _ = Nothing
equal'' :: Variant -> Variant -> Maybe Bool
equal'' = helper (==)
unequal'' :: Variant -> Variant -> Maybe Bool
unequal'' = helper (/=)
pero eso no funciona porque la variable de tipo a
aparentemente no puede vincularse a Int
y Float
al mismo tiempo en la definición de helper
; GHC lo une a Float
y luego se queja de una falta de coincidencia de tipos en la línea que maneja IntValue
.
Una función como (==)
es polimórfica cuando se usa directamente; ¿Hay alguna forma de pasarlo a otra función y hacer que siga siendo polimórfico?