Todos estos son ejemplos de "restricciones de clase": éstos limitan qué tipos se puede utilizar en lugar de la variable tipo que los sigue (a
en este caso), lo que requiere que pertenecen a un determinado type class. Ord
, Eq
y Num
son ejemplos de clases de tipos.
Ord a => ...
significa a
es un tipo que tiene una noción natural de orden asociado con él. Por ejemplo, los enteros se pueden organizar de forma natural de menor a mayor. En términos matemáticos, existe un total order en a
. Un ejemplo obvio de una función que requiere esta restricción es sort :: Ord a => [a] -> [a]
; lea esta firma que dice que sort
solo funciona en listas de cosas que pueden ordenarse entre sí.
Eq a => ...
es un tipo cuyos miembros se pueden comparar entre sí por alguna noción de igualdad. En términos matemáticos, existe un equivalence relation en a
. Tenga en cuenta que esta es una superclase de Ord
, lo que significa que cualquier cosa que tenga una noción de orden también debe tener una noción de equivalencia. Un ejemplo de una función que requiere esta restricción es elem :: Eq a => a -> [a] -> Bool
(que determina si una lista contiene un elemento dado); lea esta firma como diciendo que elem
solo funciona en listas de cosas que se pueden comparar entre sí por igualdad. Si piensas en cómo escribirías elem
, eso debería tener sentido.
Num a => ...
significa a
es un tipo numérico, lo que significa que es compatible con algunas operaciones aritméticas básicas: +
, *
, -
, abs
. Creo que esto es más o menos similar a la noción matemática de ring. Básicamente, todos los tipos que considera "tipos de número" pertenecen a esta clase: Int
, Double
, etc. Vería la restricción Num a =>
delante de una firma si la función se escribió para funcionar genéricamente con cualquier tipo de número. Por ejemplo, sum :: Num a => [a] -> a
, que suma todos los elementos de una lista de números, puede funcionar igualmente bien en [Int]
, [Double]
, [Rational]
, etc. Todo lo que tiene que hacer es sumar su contenido, sin importar qué tipo de números sean . ¡Pero los números deben ser!
Básicamente, estas clases de tipos/limitaciones son una aproximación a la "sobrecarga de principios" de funciones. Podemos usar (==) :: Eq a => a -> a -> Bool
en varios tipos, pero , no solo cualquier tipo. Algunas cosas, por ejemplo funciones, no tienen sentido comparar para igualdad (quizás porque la igualdad no es decidible para ese tipo), y nunca tiene sentido comparar dos cosas de diferentes tipos de para igualdad (contraste esto con Java, donde puede comparar dos objetos de tipos posiblemente diferentes para la igualdad).
Para obtener más lecturas (muy accesibles) sobre clases de tipo y restricciones, recomiendo Learn You a Haskell.
Creo que las personas a menudo esperan que '(==)' sea una relación de congruencia más que una relación de equivalencia. Por supuesto, Haskell tampoco te obliga a hacerlo. – augustss