{-# LANGUAGE TypeOperators , MultiParamTypeClasses , FlexibleInstances, FlexibleContexts, GeneralizedNewtypeDeriving , TypeFamilies #-}moduleData.Yall.Iso({- |
Iso is similar but more flexible than Lens in that they have no dependency on
context. This flexibility affords a number of nice class instances that we
don't get with Lens, so these can be quite useful in combination. See 'isoL'
for converting to 'Lens'.
A less imprecise name for the code here might be @Bijection@ but no one wants
to type that.
-}Iso(..),inverseI-- * Convenient Iso types-- ** Pure isomorphisms,(:<->),iso,($-),(-$)-- *** Wrapped pure Iso,IsoPure(..),ifmap,fromPure-- *** Pre-defined isomorphisms{- | Note: while all of these are pure and could be expressed as '(:<->)', we
define them polymorphically in @Monad@ for maximum flexibility in
composing with other @Lens@ or @Iso@.
Also note that for most of these @apply i . unapply i@ is not strictly
@id@, e.g. @zipI@ obviously truncates lists of differing length, etc.
This is officially not something I'm concerned about.
-},wordsI,showI,linesI,curryI,enumI,integerI,rationalI,zipI,incrementI,incrementByI,consI,distributeI,factorI-- ** Partial isomorphisms,(:<~>))whereimportPreludehiding((.),id)importControl.CategoryimportData.Functor.IdentityimportControl.Monad-- from 'categories':importqualifiedControl.Categorical.FunctorasCimportControl.Categorical.BifunctorimportControl.Category.AssociativeimportControl.Category.BraidedimportControl.Category.MonoidalimportControl.Category.Distributive-- | An Isomorphism or one-to-one mapping between types. These are very similar-- to a 'Lens', but are not dependent on context, making them more flexible. The-- functions also alow a Monadic context, supporting partial isomorphisms, and -- other interesting functionality.dataIsowmab=Iso{apply::a->mb,unapply::b->wa}instance(Monadm,Monadw)=>Category(Isowm)whereid=isoididg.f=Iso(applyf>=>applyg)(unapplyg>=>unapplyf)-- | A wrapper for a more @(->)@-like Functor instancesnewtypeIsoPureab=IsoPure{isoPure::IsoIdentityIdentityab}deriving(Category)-- ghetto deriving:pureWrapped::(IsoIdentityIdentitya1b1->IsoIdentityIdentityab)->IsoPurea1b1->IsoPureabpureWrapped2::(IsoIdentityIdentitya1b1->IsoIdentityIdentitya2b2->IsoIdentityIdentityab)->IsoPurea1b1->IsoPurea2b2->IsoPureabpureWrapped2fab=IsoPure$f(isoPurea)(isoPureb)pureWrappedf=IsoPure.f.isoPureinstancePFunctor(,)IsoPureIsoPurewherefirst=pureWrappedfirstinstanceQFunctor(,)IsoPureIsoPurewheresecond=pureWrappedsecondinstanceBifunctor(,)IsoPureIsoPureIsoPurewherebimap=pureWrapped2bimapinstancePFunctorEitherIsoPureIsoPurewherefirst=pureWrappedfirstinstanceQFunctorEitherIsoPureIsoPurewheresecond=pureWrappedsecondinstanceBifunctorEitherIsoPureIsoPureIsoPurewherebimap=pureWrapped2bimapinstanceAssociativeIsoPure(,)whereassociate=IsoPureassociateinstanceAssociativeIsoPureEitherwhereassociate=IsoPureassociateinstanceDisassociativeIsoPure(,)wheredisassociate=IsoPuredisassociateinstanceDisassociativeIsoPureEitherwheredisassociate=IsoPuredisassociateinstanceBraidedIsoPure(,)wherebraid=IsoPurebraidinstanceBraidedIsoPureEitherwherebraid=IsoPurebraidinstanceSymmetricIsoPureEitherwhereinstanceSymmetricIsoPure(,)wheretypeinstanceIdIsoPure(,)=()instanceMonoidalIsoPure(,)whereidl=IsoPureidlidr=IsoPureidrinstanceComonoidalIsoPure(,)wherecoidl=IsoPurecoidlcoidr=IsoPurecoidr-- | A more categorical 'fmap', with wrapping / unwrapping for convenience. See-- also the 'C.Functor' instances for 'Iso'.---- > ifmap = fromPure . C.fmap . IsoPureifmap::(Monadw,Monadm,C.FunctorfIsoPureIsoPure)=>IsoIdentityIdentityab->Isowm(fa)(fb)ifmap=fromPure.C.fmap.IsoPure-- | Unwrap and make polymorphic an 'IsoPure'fromPure::(Monadw,Monadm)=>IsoPureab->IsowmabfromPure(IsoPure(Isofg))=iso(fmaprunIdentityf)(fmaprunIdentityg)-- Control.Categorical.Functorinstance(Functorf)=>C.FunctorfIsoPureIsoPurewherefmap(IsoPure(Isofg))=IsoPure$iso(fmap$fmaprunIdentityf)(fmap$fmaprunIdentityg)instance(Monadm)=>C.Functorm(Isomm)(IsoIdentityIdentity)wherefmap(Isofg)=iso(>>=f)(>>=g)-- Control.Categorical.Bifunctorinstance(Monadm,Monadw)=>PFunctor(,)(Isowm)(Isowm)wherefirst=firstDefaultinstance(Monadm,Monadw)=>QFunctor(,)(Isowm)(Isowm)wheresecond=secondDefaultinstance(Monadm,Monadw)=>Bifunctor(,)(Isowm)(Isowm)(Isowm)wherebimap(Isofg)(Isof'g')=Iso(bimapMff')(bimapM'gg')-- WHY DOES TypeFamilies CAUSE PROBLEMS WITH THIS?:wherebimapMx=fmapextractJoinT.bimapxbimapM'x=fmapextractJoinT.bimapxinstance(Monadm,Monadw)=>PFunctorEither(Isowm)(Isowm)wherefirst=firstDefaultinstance(Monadm,Monadw)=>QFunctorEither(Isowm)(Isowm)wheresecond=secondDefaultinstance(Monadm,Monadw)=>BifunctorEither(Isowm)(Isowm)(Isowm)wherebimap(Isofg)(Isof'g')=Iso(bimapMff')(bimapM'gg')wherebimapMx=fmapextractJoinE.bimapxbimapM'x=fmapextractJoinE.bimapx-- Does this already exist in Categories? -- :: k (m a) (m b) -> m (k a b)-- For k = Either / (,)-- m = any MonadextractJoinE::(Monadm)=>Either(ma)(mb)->m(Eitherab)extractJoinE=either(liftMLeft)(liftMRight)extractJoinT::(Monadm)=>(ma,mb)->m(a,b)extractJoinT=uncurry$liftM2(,)-- Control.Category.Associativeinstance(Monadm,Monadw)=>Associative(Isowm)(,)whereassociate=isoassociatedisassociateinstance(Monadm,Monadw)=>Associative(Isowm)Eitherwhereassociate=isoassociatedisassociateinstance(Monadm,Monadw)=>Disassociative(Isowm)(,)wheredisassociate=isodisassociateassociateinstance(Monadm,Monadw)=>Disassociative(Isowm)Eitherwheredisassociate=isodisassociateassociate-- Control.Category.Braidedinstance(Monadm,Monadw)=>Braided(Isowm)(,)wherebraid=isobraidbraidinstance(Monadm,Monadw)=>Braided(Isowm)Eitherwherebraid=isobraidbraidinstance(Monadm,Monadw)=>Symmetric(Isowm)(,)whereinstance(Monadm,Monadw)=>Symmetric(Isowm)EitherwheredistributeI::(Monadm,Monadw)=>Isowm(a,Eitherbc)(Either(a,b)(a,c))distributeI=isodistributefactorfactorI::(Monadm,Monadw)=>Isowm(Either(a,b)(a,c))(a,Eitherbc)factorI=isofactordistribute-- Control.Category.MonoidaltypeinstanceId(Isowm)(,)=()instance(Monadm,Monadw)=>Monoidal(Isowm)(,)whereidl=isoidlcoidlidr=isoidrcoidrinstance(Monadm,Monadw)=>Comonoidal(Isowm)(,)wherecoidl=isocoidlidlcoidr=isocoidridr-- | See also an Iso wrapped in 'Dual'inverseI::(Monadm,Monadw)=>Isowmab->IsomwbainverseI(Isofg)=Isogf-- | a partial Isomorphismtypea:<~>b=IsoMaybeMaybeab-- | pure Isotypea:<->b=IsoIdentityIdentityabiso::(Monadm,Monadw)=>(a->b)->(b->a)->Isowmabisofg=Iso(fmapreturnf)(fmapreturng)-- | apply the forward function-- -- > i $- a = runIdentity $ apply i a($-)::(a:<->b)->a->bi$-a=runIdentity$applyia-- | apply the backward function-- -- > i -$ b = runIdentity $ unapply i b(-$)::(a:<->b)->b->ai-$b=runIdentity$unapplyib------------- wordsI::(Monadm,Monadw)=>IsowmString[String]wordsI=isowordsunwordslinesI::(Monadm,Monadw)=>IsowmString[String]linesI=isolinesunlinesshowI::(Reads,Shows,Monadw,Monadm)=>IsowmsStringshowI=isoshowread-- TODO or leave this as the instance above???curryI::(Monadm,Monadw)=>Isowm((a,b)->c)(a->b->c)curryI=isocurryuncurryenumI::(Enuma,Monadm,Monadw)=>IsowmIntaenumI=isotoEnumfromEnumintegerI::(Integrala,Monadm,Monadw)=>IsowmaIntegerintegerI=isotoIntegerfromIntegerrationalI::(Reala,Fractionala,Monadm,Monadw)=>IsowmaRationalrationalI=isotoRationalfromRationalzipI::(Monadm,Monadw)=>Isowm([a],[b])[(a,b)]zipI=iso(uncurryzip)unzipincrementI::(Monadm,Monadw,Numa)=>IsowmaaincrementI=incrementByI1incrementByI::(Monadm,Monadw,Numa)=>a->IsowmaaincrementByIn=iso(+n)(subtractn)-- | Calls 'fail' on the empty list.consI::(Monadm,Monadw)=>Isowm(a,[a])[a]consI=Iso(\(a,as)->return(a:as))unconsIwhereunconsI[]=fail"empty list"unconsI(a:as)=return(a,as)