如果我正確理解您的示例,則類型爲ai -> b -> ai
,而不是您第一次寫入的ai -> b -> a
。我們將其重寫爲a -> ri -> ri
,只是因爲它幫助我思考。
首先要觀察的是這種對應關係:
(a -> r1 -> r1, ..., a -> rn -> rn) ~ a -> (r1 -> r1, ..., rn -> rn)
這允許你寫這兩個函數,這是逆:
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))
第二個觀察:種形式ri -> ri
的有時也被稱爲自同態,並且這些類型中的每一個都具有組合作爲關聯操作和身份函數作爲標識的幺半羣。該Data.Monoid
包有此包裝爲:
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = id
mappend = (.)
這使您可以重寫前面pullArg
這樣:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> (Endo r1, Endo r2)
pullArg (f, g) = \a -> (Endo $ f a, Endo $ g a)
三觀察:二類羣的產品也是一個獨異,按本例如也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)
同樣,對於任何數量的參數的元組。
第四觀察:What are folds made of?答:folds are made of monoids!
import Data.Monoid
fold :: Monoid m => (a -> m) -> [a] -> m
fold f = mconcat . map f
這fold
距離Data.Foldable
的foldMap
專業化,因此在現實中,我們不需要去定義它,我們就可以導入它的更一般的版本:
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
如果fold
與Endo
,這與從右邊的摺疊。從左側摺疊,要與Dual (Endo r)
幺折:
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
記住我們pullArg
功能?讓我們來修改它多一點,因爲你從左邊摺疊:
pullArg :: (a -> r1 -> r1, a -> r2 -> r2) -> a -> Dual (Endo r1, Endo r2)
pullArg (f, g) = \a -> Dual (Endo $ f a, Endo $ g a)
而這一點,我要求,你f
的2元組版本,或者至少同構於它。你可以重構你的摺疊功能到形式a -> Endo ri
,然後執行:
let (f'1, ..., f'n) = foldMap (pullArgn f1 ... fn) xs
in (f'1 z1, ..., f'n zn)
也值得一讀:Composable Streaming Folds,這是這些想法進一步闡述。
我認爲有一個解決方案使用Arrow的***和&&&,使用像(f,(g,(h,i)))而不是(f,g,h,i)在引擎蓋下的類型,現在距離我的筆記本電腦幾百英里遠,所以今天不能玩。 – AndrewC 2014-09-26 23:32:36