------------------------------------------------------------------------------- |-- Module : Data.Generics.Twins-- Copyright : (c) The University of Glasgow, CWI 2001--2004-- License : BSD-style (see the LICENSE file)-- -- Maintainer : generics@haskell.org-- Stability : experimental-- Portability : non-portable (local universal quantification)---- \"Scrap your boilerplate\" --- Generic programming in Haskell -- See <http://www.cs.vu.nl/boilerplate/>. The present module -- provides support for multi-parameter traversal, which is also -- demonstrated with generic operations like equality.-------------------------------------------------------------------------------moduleData.Generics.Twins(-- * Generic folds and maps that also accumulategfoldlAccum,gmapAccumT,gmapAccumM,gmapAccumQl,gmapAccumQr,gmapAccumQ,-- * Mapping combinators for twin traversalgzipWithT,gzipWithM,gzipWithQ,-- * Typical twin traversalsgeq,gzip)where------------------------------------------------------------------------------#ifdef __HADDOCK__importPrelude#endifimportData.DataimportData.Generics.Aliases#ifdef __GLASGOW_HASKELL__importPreludehiding(GT)#endif---------------------------------------------------------------------------------------------------------------------------------------------------------------- Generic folds and maps that also accumulate--------------------------------------------------------------------------------{--------------------------------------------------------------
A list map can be elaborated to perform accumulation.
In the same sense, we can elaborate generic maps over terms.
We recall the type of map:
map :: (a -> b) -> [a] -> [b]
We recall the type of an accumulating map (see Data.List):
mapAccumL :: (a -> b -> (a,c)) -> a -> [b] -> (a,[c])
Applying the same scheme we obtain an accumulating gfoldl.
--------------------------------------------------------------}-- | gfoldl with accumulationgfoldlAccum::Datad=>(foraller.Datae=>a->c(e->r)->e->(a,cr))->(forallg.a->g->(a,cg))->a->d->(a,cd)gfoldlAccumkza0d=unA(gfoldlk'z'd)a0wherek'cy=A(\a->let(a',c')=unAcainka'c'y)z'f=A(\a->zaf)-- | A type constructor for accumulationnewtypeAacd=A{unA::a->(a,cd)}-- | gmapT with accumulationgmapAccumT::Datad=>(foralle.Datae=>a->e->(a,e))->a->d->(a,d)gmapAccumTfa0d0=let(a1,d1)=gfoldlAccumkza0d0in(a1,unIDd1)whereka(IDc)d=let(a',d')=fadin(a',ID(cd'))zax=(a,IDx)-- | gmapM with accumulationgmapAccumM::(Datad,Monadm)=>(foralle.Datae=>a->e->(a,me))->a->d->(a,md)gmapAccumMf=gfoldlAccumkzwherekacd=let(a',d')=fadin(a',d'>>=\d''->c>>=\c'->return(c'd''))zax=(a,returnx)-- | gmapQl with accumulationgmapAccumQl::Datad=>(r->r'->r)->r->(foralle.Datae=>a->e->(a,r'))->a->d->(a,r)gmapAccumQlor0fa0d0=let(a1,r1)=gfoldlAccumkza0d0in(a1,unCONSTr1)whereka(CONSTc)d=let(a',r)=fadin(a',CONST(c`o`r))za_=(a,CONSTr0)-- | gmapQr with accumulationgmapAccumQr::Datad=>(r'->r->r)->r->(foralle.Datae=>a->e->(a,r'))->a->d->(a,r)gmapAccumQror0fa0d0=let(a1,l)=gfoldlAccumkza0d0in(a1,unQrlr0)whereka(Qrc)d=let(a',r')=fadin(a',Qr(\r->c(r'`o`r)))za_=(a,Qrid)-- | gmapQ with accumulationgmapAccumQ::Datad=>(foralle.Datae=>a->e->(a,q))->a->d->(a,[q])gmapAccumQf=gmapAccumQr(:)[]f---------------------------------------------------------------------------------- Helper type constructors---------------------------------------------------------------------------------- | The identity type constructor needed for the definition of gmapAccumTnewtypeIDx=ID{unID::x}-- | The constant type constructor needed for the definition of gmapAccumQlnewtypeCONSTca=CONST{unCONST::c}-- | The type constructor needed for the definition of gmapAccumQrnewtypeQrra=Qr{unQr::r->r}---------------------------------------------------------------------------------- Mapping combinators for twin traversal---------------------------------------------------------------------------------- | Twin map for transformation gzipWithT::GenericQ(GenericT)->GenericQ(GenericT)gzipWithTfxy=casegmapAccumTperkidfunsyof([],c)->c_->error"gzipWithT"whereperkidad=(taila,unGT(heada)d)funs=gmapQ(\k->GT(fk))x-- | Twin map for monadic transformation gzipWithM::Monadm=>GenericQ(GenericMm)->GenericQ(GenericMm)gzipWithMfxy=casegmapAccumMperkidfunsyof([],c)->c_->error"gzipWithM"whereperkidad=(taila,unGM(heada)d)funs=gmapQ(\k->GM(fk))x-- | Twin map for queriesgzipWithQ::GenericQ(GenericQr)->GenericQ(GenericQ[r])gzipWithQfxy=casegmapAccumQperkidfunsyof([],r)->r_->error"gzipWithQ"whereperkidad=(taila,unGQ(heada)d)funs=gmapQ(\k->GQ(fk))x---------------------------------------------------------------------------------- Typical twin traversals---------------------------------------------------------------------------------- | Generic equality: an alternative to \"deriving Eq\"geq::Dataa=>a->a->Bool{-
Testing for equality of two terms goes like this. Firstly, we
establish the equality of the two top-level datatype
constructors. Secondly, we use a twin gmap combinator, namely tgmapQ,
to compare the two lists of immediate subterms.
(Note for the experts: the type of the worker geq' is rather general
but precision is recovered via the restrictive type of the top-level
operation geq. The imprecision of geq' is caused by the type system's
unability to express the type equivalence for the corresponding
couples of immediate subterms from the two given input terms.)
-}geqx0y0=geq'x0y0wheregeq'::GenericQ(GenericQBool)geq'xy=(toConstrx==toConstry)&&and(gzipWithQgeq'xy)-- | Generic zip controlled by a function with type-specific branchesgzip::GenericQ(GenericMMaybe)->GenericQ(GenericMMaybe)-- See testsuite/.../Generics/gzip.hs for an illustrationgzipfxy=fxy`orElse`iftoConstrx==toConstrythengzipWithM(gzipf)xyelseNothing