¿Por qué Bounded no es una subclase de Enum en Haskell?

9

Parece que cualquier instancia de Bounded debería tener una implementación sensata de Enum. No puedo pensar personalmente en un contraejemplo, aunque si a alguien se le ocurre uno que no sea patológico, entenderé por qué este no es el caso.

Al hacerlo :ien las dos clases de tipos, parece que la única excepción actualmente en la biblioteca estándar es para las tuplas, que son limitadas pero no enumeraciones. Sin embargo, cualquier tupla limitada también debe ser enumerable de una manera sensata, simplemente incrementando el último elemento y luego envolviéndolo cuando llegue a maxBound.

Este cambio probablemente también implicaría agregar predBy / nextBo algo así a Bounded para una forma segura / en bucle para atravesar los valores de Enum. En este caso toEnum 0 :: (...)sería igual a(toEnum 0, toEnum 0, ...) :: (...)

punto y coma
fuente
3
Realmente no puedo responder esto con autoridad, pero considere el rango de todos los números reales entre 0 y 1. Tiene límites inferiores y superiores claros, pero tiene innumerables miembros infinitos.
Doval
@Doval que es un punto justo. Sin embargo, lo mismo podría decirse de todos los números reales en general (incontables miembros infinitos), pero Double/ Floaty todos los tipos similares se implementan de Enumtodos modos, solo hacen succ = (+ 1)y fromEnum = truncate. El camino de Haskell realmente tiene sentido desde una perspectiva práctica, ya que de lo contrario [0, 0.5 ...] y similares no funcionarían, por lo que parece que a Haskell no le preocupa la contabilidad cuando se trata de Enums.
punto
1
Yo no era consciente de que succes (+1). Eso es extraño, porque Doubley Floatno tiene una precisión infinita y, por lo tanto, son enumerables, succpodría haberse definido como +1 ULP .
Doval
2
@Doval Creo que la razón de esto fue porque el equipo central de Haskell quería que [1 ..] significara lo mismo con Dobles que con Ints.
punto
@semicolon dobles y flotantes no son números reales (por ejemplo, no pueden almacenar PI en un doble sin perder algo de precisión) por lo que son enumerables
jk.

Respuestas:

8

Un ejemplo práctico que me gusta proviene del mundo de los lenguajes de programación: el conjunto de tipos en un sistema OO es limitado y discreto pero no enumerable, y está parcialmente ordenado pero no totalmente ordenado.

El ordenamiento parcial en cuestión es la relación de subtipo <:. El límite superior sería el tipo superior (que C # llama objecty llama a Scala Any), y el límite inferior sería el tipo inferior (Scala Nothing; C # / Java no tienen equivalente para hablar).

Sin embargo, no hay forma de enumerar todos los tipos en el sistema de tipos, por lo que no puede escribir un instance Enum Type. Esto debería estar claro: los usuarios pueden escribir sus propios tipos, por lo que no hay forma de saber de antemano qué serán. Puede enumerar todos los tipos en cualquier programa dado, pero no en todo el sistema.

Del mismo modo, (de acuerdo con una cierta definición razonable de subtipo), <:es reflexivo, transitivo y antisimétrico, pero no total . Hay pares de tipos que no están relacionados por <:. ( Caty Dogambos son subtipos de Animal, pero ninguno es subtipo del otro).


Supongamos que estamos escribiendo un compilador para un lenguaje OO simple. Aquí está la representación de tipos en nuestro sistema:

data Type = Bottom | Class { name :: String, parent :: Type } | Top

Y la definición de la relación de subtipo:

(<:) :: Type -> Type -> Bool
Bottom <: _ = True
Class _ _ <: Bottom = False
Class n t <: s@(Class m _)
    | n == m = True  -- you can't have different classes with the same name in this hypothetical language
    | otherwise = t <: s  -- try to find s in the parents of this class
Class _ _ <: Top = True
Top <: Top = True
Top <: _ = False

Esto también nos da una relación de supertipaje.

(>:) :: Type -> Type -> Bool
t >: s = s <: t

También puede encontrar el límite superior mínimo de dos tipos,

lub :: Type -> Type -> Type
lub Bottom s = s
lub t Bottom = t
lub t@(Class _ p) s@(Class _ q) =
    | t >: s = t
    | t <: s = s
    | p >: s = p
    | t <: q = q
    | otherwise = lub p q
lub Top _ = Top
lub _ Top = Top

Ejercicio: muestra que Typeforma un poset completo acotado de dos maneras, debajo <:y debajo >:.

Benjamin Hodgson
fuente
¡Genial gracias! Eso responde mi pregunta por completo y también responde a mi pregunta de seguimiento sobre Ord. ¿Eq tendría problemas similares? Donde un tipo no equiparable podría tener un maxBound o un minBound. En este caso, ¿Cat == Dog simplemente devolverá false, ya que son clases diferentes, o sería indecidible debido a que la posición del árbol no coloca ni por encima ni por debajo de la otra?
punto
Un orden implica una igualdad, solo define x == y = x <= y && y <= x. Si estuviera diseñando una Posetclase, lo habría hecho class Eq a => Poset a. Un rápido Google confirma que otras personas han tenido la misma idea .
Benjamin Hodgson
Lo siento, mi pregunta era ambigua. Lo que quise decir es si Bounded implica Eq incluso si no implica Ord.
punto
@semicolon Nuevamente, no hay relación entre las dos clases. Considere data Bound a = Min | Val a | Maxqué aumenta un tipo acon +∞y -∞elementos. Por construcción Bound asiempre se puede hacer una instancia de, Boundedpero solo sería equiparable si el tipo subyacente aes
Benjamin Hodgson
Muy bien. Creo que un ejemplo podría ser las funciones que toman y los valores de retorno de tipo Double, donde const (1/0)es maxBoundy const (negate 1/0)es minBoundpero \x -> 1 - xy \x -> x - 1son incomparables.
punto
4

Es porque las operaciones son independientes, por lo que vincularlas con una relación de subclase en realidad no te compra nada. Supongamos que desea crear un tipo personalizado que se implemente Bounded, tal vez Doubleslimitado entre un máximo y un mínimo, pero no tuvo necesidad de ninguna de las Enumoperaciones. Si Boundedfuera una subclase, tendría que implementar todas las Enumfunciones de todos modos, solo para que se compile.

Realmente no importa si hay una implementación razonable Enumo cualquier otro número de clases de tipos. Si realmente no lo necesita, no debería verse obligado a implementarlo.

Contraste esto con decir, Ordy Eq. Allí, las Ordoperaciones dependen de las Eqmismas, por lo que tiene sentido exigir a la subclase para evitar la duplicación y garantizar la coherencia.

Karl Bielefeldt
fuente
1
En esos casos, es parte de la definición. Todas las mónadas también son solicitantes y functores por definición, por lo que no puede cumplir el "contrato" de la mónada sin cumplir con los demás. No estoy lo suficientemente familiarizado con las matemáticas para saber si esa es una relación fundamental o una definición impuesta, pero de cualquier manera, estamos atrapados con eso ahora.
Karl Bielefeldt
66
@semicolon La documentación deBounded dice "Ord no es una superclase de Bounded ya que los tipos que no están totalmente ordenados también pueden tener límites superior e inferior".
Benjamin Hodgson
1
@BenjaminHodgson Ni siquiera pensé en los tipos parcialmente ordenados. +1 para un ejemplo no patológico y para citar la documentación.
Doval
1
@semicolon Un ejemplo de un pedido parcial del mundo de las computadoras podría estar subtipando en lenguajes OO. Escribir <:para es un subtipo de , ∀ T S. T <: S ∨ S <: Tno se cumple (por ejemplo, int !<: bool ∧ bool !<: int). Probablemente te encontrarás con esto si estuvieras escribiendo un compilador.
Benjamin Hodgson
1
@BenjaminHodgson ah ok. Entonces, por ejemplo, si A es una superclase de B y C, y D es una subclase de B y C, entonces B y C son incomparables, pero ¿A y D son max / min?
punto