Esaminerò l'esempio dei tipi di dati principali nelle tecniche di ordinamento radix generalizzato di Fritz Henglein implementate da Edward Kmett nel pacchetto discrimination.
Mentre c'è una grande quantità succedendo lì, si concentra in gran parte intorno a un tipo come questo
data Group a = Group (forall b . [(a, b)] -> [[b]])
Se si dispone di un valore di tipo Group a
voi essenzialmente necessario disporre di una relazione di equivalenza su a
perché se ti do un'associazione tra a
s e qualche tipo b
completamente sconosciuto a voi quindi potete darmi "raggruppamenti" di b
.
groupId :: Group a -> [a] -> [[a]]
groupId (Group grouper) = grouper . map (\a -> (a, a))
Si può vedere questo come un tipo di base per scrivere una libreria di utilità di raggruppamenti. Per esempio, potremmo voler sapere che se siamo in grado di Group a
e Group b
allora possiamo Group (a, b)
(di più su questo in un secondo). L'idea di base di Henglein è che se è possibile iniziare con alcuni numeri di base Group
su interi - possiamo scrivere implementazioni di tipo Group Int32
molto veloci tramite ordinamento digitale - e quindi utilizzare i combinatori per estenderli a tutti i tipi, quindi ordinare i tipi di dati algebrico generalizzati.
Quindi, come possiamo creare la nostra libreria combinatore?
Beh, f :: Group a -> Group b -> Group (a, b)
è piuttosto importante in quanto ci consente di creare gruppi di tipi simili a prodotti. Normalmente, otterremmo questo da Applicative
e liftA2
ma Group
, noterete, è , non uno Functor
.
Così, invece usiamo Divisible
divided :: Group a -> Group b -> Group (a, b)
Si noti che questo si pone in uno strano modo da
divide :: (a -> (b, c)) -> Group b -> Group c -> Group a
in quanto ha il carattere tipico "freccia rovesciata" delle cose controvarianti. Ora possiamo capire cose come divide
e conquer
in termini di interpretazione su Group
.
Divide dice che se voglio costruire una strategia per equiparare a
s utilizzando strategie per equiparare b
s e c
s, posso effettuare le seguenti operazioni per ogni tipo x
prendere il vostro rapporto parziale [(a, x)]
e mappare su di esso con una funzione f :: a -> (b, c)
e una piccola manipolazione della tupla, per ottenere una nuova relazione [(b, (c, x))]
.
Usa mia Group b
di discriminare [(b, (c, x))]
in [[(c, x)]]
Usa il mio Group c
di discriminare ogni [(c, x)]
in [[x]]
darmi [[[x]]]
appiattire lo strati interni per ottenere [[x]]
come abbiamo bisogno
instance Divisible Group where
conquer = Group $ return . fmap snd
divide k (Group l) (Group r) = Group $ \xs ->
-- a bit more cleverly done here...
l [ (b, (c, d)) | (a,d) <- xs, let (b, c) = k a] >>= r
Abbiamo anche ottenere interpretazioni del più complicato Decidable
refinement of Divisible
class Divisible f => Decidable f where
lose :: (a -> Void) -> f a
choose :: (a -> Either b c) -> f b -> f c -> f a
instance Decidable Group where
lose :: (a -> Void) -> Group a
choose :: (a -> Either b c) -> Group b -> Group c -> Group a
Questi lette come dire che per qualsiasi tipo a
di cui siamo in grado di garantire non ci sono valori (non siamo in grado di produrre valori di Void
con qualsiasi mezzo, una funzione a -> Void
è un mezzo per produrre Void
dato a
, quindi non dobbiamo essere in grado di produrre valori di a
con qualsiasi mezzo o!), subito ci un raggruppamento di valori zero
lose _ = Group (\_ -> [])
Possiamo anche andare ad un gioco simile a quello divide
eccetto che invece di sequenziare il nostro uso dei discriminatori di input, ci alterniamo.
Usando queste tecniche costruiamo una libreria di "Group
capaci" le cose, vale a dire Grouping
class Grouping a where
grouping :: Group a
e notiamo che nearly all the definitions arise dalla definizione di base in cima groupingNat
che utilizza veloci manipuations vettore monadici per raggiungere un efficiente radix sort.
Ed Kmett sta lavorando per rendere pratiche le generalizzazioni di Radix sort di Fritz Henglein. Si scopre che questi "discriminatori" sono Divisibili naturali e gran parte della tecnica può essere scritta lungo le linee di quella classe di caratteri https://hackage.haskell.org/package/discrimination –
Oh wow, è impressionante. Questo è praticamente l'ultimo posto in cui avrei mai pensato di usare una classe di caratteri. – TheSeamau5
@ J.Abrahamson dovresti formulare il tuo commento in una risposta perché merita tutti gli upvotes. Soprattutto se includi esempi di codice^_^ – frasertweedale