Perché non è Limitato una sottoclasse di Enum in Haskell

6

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

Dal fare :i sui due typeclass sembra che l'unica eccezione attualmente nella libreria standard sia per le tuple, che sono Bounded ma non Enums. Tuttavia, qualsiasi tupla Bounded deve anche essere Enumerable in modo corretto, semplicemente incrementando l'ultimo elemento e quindi avvolgendolo quando arriva a maxBound.

Questo cambiamento probabilmente implicherebbe anche l'aggiunta di predB e nextB o qualcosa del genere a Bounded per un modo sicuro / ciclico per attraversare i valori di Enum. In questo caso toEnum 0 :: (...) sarebbe uguale a (toEnum 0, toEnum 0, ...) :: (...)

    
posta semicolon 30.03.2016 - 00:39
fonte

2 risposte

6

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

L'ordine parziale in questione è la relazione di sottotipizzazione <: . Il limite superiore sarebbe quindi il tipo superiore (che C # chiama object e Scala chiama Any ), e il limite inferiore sarebbe il tipo inferiore (Scala Nothing ; C # / Java non hanno l'equivalente di parlare di).

Tuttavia, non esiste un modo per enumerare tutti i tipi nel sistema di 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 un determinato 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 correlati da <: . ( Cat e Dog sono entrambi sottotipi di Animal , ma nessuno dei due è un sottotipo dell'altro.)

Supponiamo che stiamo scrivendo 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 sottotipizzazione:

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

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

Puoi anche trovare il minimo superiore 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 Type forma un post completo completo limitato in due modi, sotto <: e sotto >: .

    
risposta data 30.03.2016 - 19:13
fonte
3

È perché le operazioni sono indipendenti, quindi legarle insieme a una relazione di sottoclasse non ti compra nulla. Supponiamo che tu voglia creare un tipo personalizzato che ha implementato Bounded , forse Doubles vincolato tra un massimo e un minimo, ma non hai avuto bisogno di nessuna delle operazioni Enum . Se Bounded fosse una sottoclasse, dovresti comunque implementare tutte le funzioni Enum , solo per farlo compilare.

Non importa se esiste un'implementazione ragionevole per Enum o qualsiasi altro numero di typeclass. Se non ne hai effettivamente bisogno, non dovresti essere costretto a implementarlo.

Contrastare questo con dire, Ord e Eq . Lì, le operazioni Ord dipendono da quelle Eq , quindi ha senso richiedere la sottoclasse per evitare la duplicazione e garantire la coerenza.

    
risposta data 30.03.2016 - 05:25
fonte

Leggi altre domande sui tag