Perché non è associato una sottoclasse di Enum in Haskell


9

Sembra che ogni istanza rilegata dovrebbe avere un'implementazione sana di Enum. Non riesco a pensare personalmente a un controesempio, anche se se qualcuno ne trova uno che non è patologico, capirò perché non è così.

Dal fare :isui due caratteri tipografici sembra che l'unica eccezione attualmente nella libreria standard sia per le tuple, che sono limitate ma non Enum. Tuttavia, qualsiasi tupla legata deve anche essere enumerabile in modo sano, semplicemente incrementando l'ultimo elemento e poi avvolgendosi quando arriva a maxBound.

Questo cambiamento sarebbe probabilmente coinvolgere anche aggiungere predBe nextBo qualcosa di simile ad Delimitata per una cassetta di sicurezza / loop modo di attraversare attraverso i valori Enum. In questo caso toEnum 0 :: (...)sarebbe uguale a(toEnum 0, toEnum 0, ...) :: (...)


3
Non posso davvero rispondere in modo autorevole, ma considera l'intervallo di tutti i numeri reali tra 0 e 1. Ha chiari limiti inferiore e superiore ma ha membri infinitamente innumerevoli.
Doval,

@Doval questo è un punto giusto. Tuttavia, si potrebbe dire lo stesso di tutti i numeri reali in generale (membri infiniti infiniti), ma Double/ Floate tutti i tipi simili implementano Enumcomunque, semplicemente fanno succ = (+ 1)e fromEnum = truncate. Il modo di Haskell ha davvero senso dal punto di vista della praticità, altrimenti [0, 0,5 ..] e simili non funzionerebbero, quindi sembra che Haskell non si preoccupi della numerabilità quando si tratta di Enums.
punto

1
Non sapevo che lo succfosse (+1). È strano, perché Doublee Floatnon hanno una precisione infinita e quindi sono enumerabili - succavrebbe potuto essere definito come +1 ULP .
Doval,

2
@Doval Penso che il motivo sia dovuto al fatto che il core team di Haskell voleva [1 ..] significare la stessa cosa con Doubles che significa con Ints.
punto

I doppi e i float di @semicolon non sono numeri reali (ad es. non è possibile memorizzare PI in doppio senza perdere una certa precisione) quindi sono enumerabili
jk.

Risposte:


8

Un esempio pratico che mi piace viene dal mondo dei linguaggi di programmazione: l'insieme di tipi in un sistema OO è limitato e discreto ma non enumerabile, e parzialmente ordinato ma non completamente ordinato.

L'ordinamento parziale in questione è la relazione di sottotipizzazione <:. Il limite superiore sarebbe quindi il tipo superiore (che chiama C # objecte Scala chiama Any) e il limite inferiore sarebbe il tipo inferiore (Scala's Nothing; C # / Java non hanno equivalenti di cui parlare).

Tuttavia, non è possibile enumerare tutti i tipi nel sistema dei tipi, quindi non è possibile scrivere un instance Enum Type. Questo dovrebbe essere chiaro: gli utenti possono scrivere i propri tipi, quindi non c'è modo di sapere cosa saranno in anticipo. Puoi enumerare tutti i tipi in qualsiasi programma, ma non nell'intero sistema.

Allo stesso modo, (secondo una certa ragionevole definizione di sottotipizzazione) <:è riflessivo, transitivo e antisimmetrico ma non totale . Esistono coppie di tipi non correlate da <:. ( Cate Dogsono entrambi sottotipi di Animal, ma nessuno dei due è un sottotipo dell'altro.)


Supponiamo di scrivere un compilatore per un semplice linguaggio OO. Ecco la rappresentazione dei tipi nel nostro sistema:

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

E la definizione della relazione di sottotipo:

(<:) :: 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

Questo ci dà anche una relazione supertipica.

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

Puoi anche trovare il limite superiore minimo di due tipi,

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

Esercizio: mostra che Typeforma un poset completo limitato in due modi, sotto <:e sotto >:.


Fantastico grazie! Che risponde completamente alla mia domanda e risponde anche alla mia domanda di follow-up su Ord. L'Eq avrebbe problemi simili? Dove un tipo non equabile potrebbe avere un maxBound o un minBound. In questo caso Cat == Dog dovrebbe solo restituire false, poiché sono classi diverse, o sarebbe indecidibile a causa della posizione dell'albero che non pone né sopra né sotto l'altra?
punto

Un ordinamento implica un'uguaglianza - basta definire x == y = x <= y && y <= x. Se stessi progettando una Posetclasse, avrei class Eq a => Poset a. Un rapido Google conferma che altre persone hanno avuto la stessa idea .
Benjamin Hodgson,

Mi dispiace che la mia domanda sia stata ambigua. Ciò che intendevo era se Bounded implicava l'Eq anche se non implica Ord.
punto

@semicolon Ancora una volta non c'è relazione tra le due classi. Considera data Bound a = Min | Val a | Maxquale aumenta un tipo acon +∞e gli -∞elementi. Per costruzione Bound apuò sempre essere un'istanza di Boundedma sarebbe equabile solo se il tipo sottostante aè
Benjamin Hodgson

va bene abbastanza. Immagino che un esempio potrebbe essere le funzioni che accettano e restituiscono valori di tipo Double, dove const (1/0)è maxBounded const (negate 1/0)è minBoundma \x -> 1 - xe \x -> x - 1sono incomparabili.
punto

4

È perché le operazioni sono indipendenti, quindi collegarle con una relazione di sottoclasse non ti compra nulla. Supponiamo che tu voglia creare un tipo personalizzato implementato Bounded, magari Doublesvincolato tra un massimo e un minimo, ma non hai bisogno di nessuna delle Enumoperazioni. Se Boundedfosse una sottoclasse, dovresti Enumcomunque implementare tutte le funzioni, solo per farlo compilare.

Non importa se esiste un'implementazione ragionevole per Enum, o qualsiasi altro numero di macchine da scrivere. Se in realtà non ne hai bisogno, non dovresti essere costretto a implementarlo.

Contrastare questo con dire, Orde Eq. Lì, le Ordoperazioni dipendono da Eqquelle, quindi ha senso richiedere la sottoclasse per evitare duplicazioni e garantire coerenza.


1
In questi casi, fa parte della definizione. Tutte le monadi sono anche applicativi e funzionali per definizione, quindi non puoi adempiere al "contratto" di monade senza adempiere agli altri. Non ho abbastanza familiarità con la matematica per sapere se si tratta di una relazione fondamentale o di una definizione imposta, ma in entrambi i casi, ora ci siamo bloccati.
Karl Bielefeldt,

6
@semicolon La documentazione perBounded dice "Ord non è una superclasse di Bounded poiché i tipi che non sono totalmente ordinati possono avere anche limiti superiore e inferiore".
Benjamin Hodgson,

1
@BenjaminHodgson Non pensavo nemmeno ai tipi parzialmente ordinati. +1 per un esempio non patologico e per citare la documentazione.
Doval,

1
@semicolon Un esempio di un ordinamento parziale dal mondo dei computer potrebbe essere il sottotitolo nelle lingue OO. Scrivere <:per è un sottotipo di , ∀ T S. T <: S ∨ S <: Tnon regge (es., int !<: bool ∧ bool !<: int). Probabilmente ti imbatteresti in questo se scrivessi un compilatore.
Benjamin Hodgson,

1
@BenjaminHodgson ah ok. Quindi, ad esempio, se A è una superclasse di B e C e D è una sottoclasse di B e C, allora B e C sono incomparabili ma A e D sono max / min?
punto
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.