Set
, analogamente a []
ha una operazione monadica perfettamente definita. Il problema è che richiedono che i valori soddisfino il vincolo Ord
e quindi è impossibile definire return
e >>=
senza alcun vincolo. Lo stesso problema si applica a molte altre strutture di dati che richiedono alcuni tipi di vincoli sui valori possibili.Costruire istanze monade efficienti su `Set` (e altri contenitori con vincoli) usando il monad di continuazione
Il trucco standard (suggeritomi in un haskell-cafe post) è quello di avvolgere Set
nella monade di continuazione. ContT
non si preoccupa se il functor di tipo sottostante ha vincoli. I vincoli diventano necessarie solo quando la confezione/scartare Set
s in/da continuazioni:
import Control.Monad.Cont
import Data.Foldable (foldrM)
import Data.Set
setReturn :: a -> Set a
setReturn = singleton
setBind :: (Ord b) => Set a -> (a -> Set b) -> Set b
setBind set f = foldl' (\s -> union s . f) empty set
type SetM r a = ContT r Set a
fromSet :: (Ord r) => Set a -> SetM r a
fromSet = ContT . setBind
toSet :: SetM r r -> Set r
toSet c = runContT c setReturn
Questo funziona in base alle esigenze. Per esempio, possiamo simulare una funzione non deterministica che o aumenta la sua tesi di 1 o lascia intatto:
step :: (Ord r) => Int -> SetM r Int
step i = fromSet $ fromList [i, i + 1]
-- repeated application of step:
stepN :: Int -> Int -> Set Int
stepN times start = toSet $ foldrM ($) start (replicate times step)
Infatti, stepN 5 0
cede fromList [0,1,2,3,4,5]
. Se abbiamo usato []
monade, invece, otterremmo
[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5]
invece.
Il problema è efficienza. Se chiamiamo stepN 20 0
l'output richiede alcuni secondi e stepN 30 0
non termina entro un ragionevole lasso di tempo. Si scopre che tutte le operazioni Set.union
vengono eseguite alla fine, invece di eseguirle dopo ogni calcolo monadico. Il risultato è che in modo esponenziale vengono costruiti molti Set
e union
solo alla fine, il che è inaccettabile per la maggior parte delle attività.
Esiste un modo per aggirare questa struttura? Ho provato ma senza successo.
(ho anche il sospetto che ci potrebbero essere alcuni tipi di limiti teorici seguito da Curry-Howard isomorfismo e Glivenko's theorem. Il teorema di Glivenko dice che per ogni tautologia proposizionale φ la formula ¬¬φ può essere provato in logica intuizionista . Tuttavia, ho il sospetto che la lunghezza della prova (in forma normale) può essere esponenzialmente lungo. Così, forse, ci potrebbero essere casi in cui avvolgere un calcolo nella monade prosecuzione renderà esponenzialmente più?)
Beh, mi sembra che non ci può essere un 'un'istanza Monad' veramente efficace per' set' a meno che non ci sia anche un'istanza 'Functor' efficiente. E sto facendo fatica a vedere come si può avere un 'fmap' efficiente per' Set'. [L'esistente 'map' per' Set' è n * log n.] (Http://hackage.haskell.org/packages/archive/containers/0.4.2.1/doc/html/Data-Set.html # g: 7) 'Set' è implementato come alberi rigorosi, quindi la pigrizia non ti aiuterà mai. –
Penso che il problema sia che la monade non "sa" che i numeri hanno "Ord" o anche "Eq". – PyRulez
@LuisCasillas Un ulteriore fattore _log n_ sarebbe OK, la cosa che mi preoccupa è l'esplosione esponenziale. –