Se ho capito bene i vostri esempi, i tipi sono ai -> b -> ai
, non ai -> b -> a
come avete scritto per la prima volta. Riscriviamo i tipi a a -> ri -> ri
, solo perché mi aiuta a pensare.
La prima cosa da osservare è questa corrispondenza:
(a -> r1 -> r1, ..., a -> rn -> rn) ~ a -> (r1 -> r1, ..., rn -> rn)
Questo permette di scrivere queste due funzioni, che sono inverse:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (r1 -> r1, r2 -> r2)
pullArg (f, g) = \a -> (f a, g a)
pushArg :: (a -> (r1 -> r1, r2 -> r2)) -> (a -> r1 -> r1, a -> r2 -> r2)
pushArg f = (\a -> fst (f a), \a -> snd (f a))
Seconda osservazione: i tipi di forma ri -> ri
sono a volte chiamati endomorfismi e ognuno di questi tipi ha un monoide con composizione come operazione associativa e funzione di identità come identità. Il pacchetto Data.Monoid
ha questo wrapper per questo: si
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend = (.)
Questo permette di riscrivere la precedente pullArg
a questo:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (Endo r1, Endo r2)
pullArg (f, g) = \a -> (Endo $ f a, Endo $ g a)
Terza osservazione: il prodotto di due monoidi è anche un monoide, di cui al presente esempio anche da Data.Monoid
:
instance (Monoid a, Monoid b) => Monoid (a, b) where
mempty = (mempty, mempty)
(a, b) `mappend` (c, d) = (a `mappend` c, b `mappend d)
Analogamente per le tuple di qualsiasi numero di argomenti.
Quarta osservazione: What are folds made of? Risposta: folds are made of monoids!
import Data.Monoid
fold :: Monoid m => (a -> m) -> [a] -> m
fold f = mconcat . map f
Questo fold
è solo una specializzazione di foldMap
da Data.Foldable
, quindi in realtà non abbiamo bisogno di definirlo, possiamo semplicemente importare la sua versione più generale:
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
Se fold
con Endo
, che è lo stesso di piegare da destra.Per piegare da sinistra, si vuole piegare con la Dual (Endo r)
monoide:
myfoldl :: (a -> Dual (Endo r)) -> r -> -> [a] -> r
myfoldl f z xs = appEndo (getDual (fold f xs)) z
-- From `Data.Monoid`. This just flips the order of `mappend`.
newtype Dual m = Dual { getDual :: m }
instance Monoid m => Monoid (Dual m) where
mempty = Dual mempty
Dual a `mappend` Dual b = Dual $ b `mappend` a
Ricordate la nostra funzione pullArg
? Facciamo rivedere un po 'di più, dal momento che si sta piegando da sinistra:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> Dual (Endo r1, Endo r2)
pullArg (f, g) = \a -> Dual (Endo $ f a, Endo $ g a)
E questo, io sostengo, è la versione 2-tuple del f
, o almeno isomorfo ad esso. È possibile riconfigurare le funzioni piega nella forma a -> Endo ri
, e poi fare:
let (f'1, ..., f'n) = foldMap (pullArgn f1 ... fn) xs
in (f'1 z1, ..., f'n zn)
vale la pena anche guardare: Composable Streaming Folds, che è una ulteriore elaborazione di queste idee.
Penso che ci sia una soluzione usando Arrow's e &&&, usando tipi come (f, (g, (h, i)) invece di (f, g, h, i) sotto il cofano, ma io ' A un paio di centinaia di miglia dal mio portatile al momento, quindi non posso giocare oggi. – AndrewC