Quando si tratta di applicare la teoria delle categorie per la programmazione generica, Haskell fa un ottimo lavoro, ad esempio con librerie come recursion-schemes
. Tuttavia una cosa di cui non sono sicuro è come creare un'istanza di functor generica per tipi polimorfici.Istanza di Functor per ADT polimorfici generici in Haskell?
Se si dispone di un tipo polimorfico, come un elenco o un albero, è possibile creare un functor da (Hask × Hask) a Hask che li rappresenta. Per esempio:
data ListF a b = NilF | ConsF a b -- L(A,B) = 1+A×B
data TreeF a b = EmptyF | NodeF a b b -- T(A,B) = 1+A×B×B
Questi tipi sono polimorfici in A, ma vengono fissati i punti per quanto riguarda B, qualcosa di simile:
newtype Fix f = Fix { unFix :: f (Fix f) }
type List a = Fix (ListF a)
type Tree a = Fix (TreeF a)
Ma, come molti sanno, gli elenchi e gli alberi sono anche funtori nel senso comune del termine, dove rappresentano un "contenitore" di a
, che è possibile mappare una funzione f :: a -> b
per ottenere un contenitore di b
.
Sto cercando di capire se c'è un modo per rendere questi tipi (i punti fissi) un'istanza di Functor
in un modo generico, ma non sono sicuro di come. Ho incontrato i seguenti 2 problemi finora:
1) In primo luogo, ci deve essere un modo per definire una generica gmap
su qualsiasi punto fisso polimorfico. Sapendo che tipi come ListF
e TreeF
sono Bifunctors, finora ho ottenuto questo:
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Bifunctor
newtype Fix f = Fix { unFix :: f (Fix f) }
cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = f . fmap (cata f) . unFix
-- To explicitly use inF as the initial algebra
inF :: f (Fix f) -> Fix f
inF = Fix
gmap :: forall a b f. Bifunctor f => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
where
alg :: f a (Fix (f b)) -> Fix (f b)
alg = inF . bimap f id
In Haskell questo mi dà il seguente errore: Could not deduce (Functor (f a)) arising from a use of cata from the context (Bifunctor f)
.
Sto utilizzando il pacchetto bifunctors
, che ha un tipo WrappedBifunctor
che definisce in modo specifico la seguente istanza che potrebbe risolvere il problema precedente: Bifunctor p => Functor (WrappedBifunctor p a)
. Tuttavia, non sono sicuro di come "sollevare" questo tipo all'interno Fix
per essere in grado di usarlo
2) Anche se il generico gmap
sopra può essere definita, non so se è possibile creare un'istanza generica di Functor
che dispone di fmap = gmap
e può funzionare immediatamente sia per i tipi List
e Tree
lassù (così come qualsiasi altro tipo definito in modo simile). È possibile?
Se sì, sarebbe possibile renderlo compatibile anche con recursion-schemes
?
Questo è il tuo ricordo di legge che, come "ADT" sta sia per "tipo di dati algebrici" e "tipo di dati astratti", e poiché le due nozioni sono violentemente opposte, il termine "ADT" è privo di senso e meglio evitato. – pigworker
@pigworker: che ne dici se li chiamiamo uGADT d'ora in poi? – leftaroundabout