yall 0.1 → 0.2
raw patch · 4 files changed
+115/−71 lines, 4 files
Files
- Data/Yall.hs +1/−1
- Data/Yall/Iso.hs +2/−2
- Data/Yall/Lens.hs +103/−66
- yall.cabal +9/−2
Data/Yall.hs view
@@ -31,7 +31,7 @@ getM = L.getM -- | try to run the setter function on an outer and new inner value-setM :: (a L.:~> b) -> b -> a -> Maybe a+setM :: (a L.:~> b) -> a -> b -> Maybe a setM = L.setM -- | try to modify the inner type of a value
Data/Yall/Iso.hs view
@@ -24,8 +24,8 @@ 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.+ @id@ for the entire input domain, e.g. @zipI@ obviously truncates lists of+ differing length, etc. -} , wordsI, showI, linesI, curryI, enumI, integerI, rationalI, zipI , incrementI, incrementByI, consI
Data/Yall/Lens.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE MultiParamTypeClasses, TypeOperators, TypeFamilies , FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses, TypeOperators, TypeFamilies , FlexibleInstances, FlexibleContexts #-} module Data.Yall.Lens ( {- | The Lenses here are parameterized over two Monads (by convention @m@ and@@ -46,21 +46,21 @@ -- [[('a',0),('b',2),('c',3)],[('a',1),('b',0),('c',3)],[('a',1),('b',2),('c',0)]] - -- ** Lenses with monadic getters+ , Lenses(..)+ , LensM , lensM- , getM, setM, modifyM -- ** Monadic variants {- | The setter continuation is embedded in the getter\'s Monadic environment, so we offer several ways of combining different types of- getter environments(@m@) and setter environments (@w@), for Lenses+ getter environments (@m@) and setter environments (@w@), for Lenses with complex effects.++ Newtype wrappers around 'Lens' let us use the same 'Lenses' interface+ for getting and setting for these various monad-combining schemes. -} , lensMW- , setW, modifyW- , setLiftM, setLiftW, setJoin- -- *** Monoid setters- , setEmpty, setEmptyM, setEmptyW+ , LensLift(..) , LensJoin(..) , LensW(..) -- * Composing Lenses {- |@@ -101,7 +101,8 @@ , fstL, sndL , eitherL, (|||) , factorL, distributeL- , isoL+ -- ** Lenses from Isomorphisms+ , isoL , residualL -- * Convenience operators {- | The little \"^\" hats are actually superscript \"L\"s (for "Lens") that have fallen over.@@ -137,7 +138,6 @@ import Control.Monad import Control.Monad.Trans.Class import Data.Functor.Identity-import Data.Monoid {-@@ -148,34 +148,39 @@ -- constrain these 'm's to Monad? newtype Lens w m a b = Lens { runLens :: a -> m (b -> w a, b) } --- | A lens in which the setter returns its result in the trivial identity --- monad. This is appropriate e.g. for traditional partial lenses, where there is--- a potential that the lens could fail only on the /outer/ constructor.+-- | A lens in which the setter returns its result in the trivial 'Identity'+-- monad. This is appropriate e.g. for traditional partial lenses on sum types,+-- where there is a potential that the lens could fail only on the /outer/+-- constructor. type LensM = Lens Identity -- | Create a monadic lens from a getter and setter lensM :: (Monad m)=> (a -> m b) -> (a -> m (b -> a)) -> LensM m a b lensM g = lensMW g . fmap (liftM $ fmap return) --- | get, returning the result in a Monadic environment. This is appropriate--- e.g. for traditional partial lenses on multi-constructor types. See also--- 'setM'-getM :: (Monad m)=> Lens w m a b -> a -> m b-getM (Lens f) = liftM snd . f+-- | A class for our basic (monadic) lens operations. Minimal complete+-- definition is 'getM' and 'setM'+class (Monad m)=> Lenses l m where+ getM :: l m a b -> a -> m b+ setM :: l m a b -> a -> b -> m a+ modifyM :: l m a b -> (b -> b) -> a -> m a+ modifyM l f a = getM l a >>= setM l a . f --- | set, returning the result in the getter\'s Monadic environment, running --- the setter\'s trivial Identity monad. -setM :: (Monad m)=> LensM m a b -> b -> a -> m a-setM (Lens f) b = liftM (runIdentity . ($ b) . fst) . f+-- helpers:+getterM :: Monad m => Lens t m a r -> a -> m r+getterM (Lens f) = liftM snd . f+setterM :: Monad m => Lens t m a b -> a -> m (b -> t a)+setterM (Lens f) = liftM fst . f --- | modify the inner value within the getter\'s Monadic environment -modifyM :: (Monad m)=> LensM m a b -> (b -> b) -> a -> m a-modifyM (Lens f) g a = do- (bWa, b) <- f a- return (runIdentity $ bWa $ g b)+instance (Monad m)=> Lenses (Lens Identity) m where+ getM = getterM+ -- is let-floating effective here? Can snd be GCed after 'setM l a'?+ setM l a = let mba = setterM l a+ in \b-> liftM (runIdentity . ($ b)) mba+ modifyM (Lens f) g a = do+ (bWa, b) <- f a+ return (runIdentity $ bWa $ g b) -modifyW :: (Monad w)=> Lens w Identity a b -> (b -> b) -> a -> w a-modifyW (Lens f) g = uncurry ($) . second g . runIdentity . f -- | Create a monadic Lens from a setter and getter. --@@ -183,26 +188,44 @@ lensMW :: (Monad m)=> (a -> m b) -> (a -> m (b -> w a)) -> Lens w m a b lensMW g s = Lens $ \a-> liftM2 (,) (s a) (g a) --- | set, with Monadic setter & pure getter-setW :: (Monad w)=> Lens w Identity a b -> b -> a -> w a-setW (Lens f) b = ($ b) . fst . runIdentity . f --- | set, 'lift'ing the outer (getter\'s) Monadic environment to the type of--- the setter monad transformer.-setLiftM :: (Monad (t m), MonadTrans t, Monad m)=> Lens (t m) m a b -> b -> a -> t m a -setLiftM (Lens f) b = join . liftM (($ b) . fst) . lift . f +------------------ MONADIC VARIANTS: ---------------------+-- TODO: derive all classes --- | set, like 'setLiftM' but we 'lift' the /inner/ setter\'s environment to--- the outer getter monad transformer.-setLiftW :: (MonadTrans t, Monad (t w), Monad w)=> Lens w (t w) a b -> b -> a -> t w a-setLiftW (Lens f) b a = lift . ($ b) . fst =<< f a +-- | lenses in which set/get should 'lift' the inner monad @w@ to @m@+newtype LensLift w m a b = LLift (Lens w m a b) +instance (MonadTrans t, Monad (t w), Monad w)=> Lenses (LensLift w) (t w) where+ getM (LLift l) = getterM l+ setM (LLift l) a = let mba = setterM l a+ in \b-> lift . ($ b) =<< mba+++-- | lenses in which @m@ == @w@ and we would like to 'join' the two in get/set+newtype LensJoin m a b = LJoin (Lens m m a b)++instance (Monad m)=> Lenses LensJoin m where+ getM (LJoin l) = getterM l+ setM (LJoin l) a = let mba = setterM l a+ in \b-> mba >>= ($ b)++-- | lenses in which only the setter @w@ is monadic+newtype LensW w a b = LW (Lens w Identity a b)++instance (Monad w)=> Lenses LensW w where+ getM (LW l) = return . get l+ setM (LW (Lens f)) a = let bwa = fst $ runIdentity $ f a+ in bwa+ modifyM (LW (Lens f)) g = uncurry ($) . second g . runIdentity . f+++-- -- | set, 'lift'ing the outer (getter\'s) Monadic environment to the type of+-- -- the setter monad transformer.+-- setLiftM :: (Monad (t m), MonadTrans t, Monad m)=> Lens (t m) m a b -> b -> a -> t m a +-- setLiftM (Lens f) b = join . liftM (($ b) . fst) . lift . f + --- | set, combining the effects of the identical setter and getter Monads with--- 'join'.-setJoin :: (Monad m)=> Lens m m a b -> b -> a -> m a-setJoin (Lens f) b a = f a >>= ($ b) . fst instance (Monad w, Monad m)=> Category (Lens w m) where id = Lens $ return . (,) return@@ -322,10 +345,17 @@ --- | Convert an isomorphism to a 'Lens'+-- | Convert an isomorphism @i@ to a 'Lens'. When @apply i . unapply i =+-- unapply i . apply i = id@, the resulting lens will be well-behaved. isoL :: (Monad m, Monad w)=> Iso m w a b -> Lens m w a b isoL (Iso f g) = Lens $ fmap (liftM ((,) g)) f +-- | Convert to a Lens an isomorphism between a value @a@ and a tuple of a+-- value @b@ with some \"residual\" value @r@. +residualL :: (Monad m, Monad w)=> Iso m w a (b,r) -> Lens m w a b+residualL (Iso f g) = Lens $ \a-> do+ (b,r) <- f a+ return (\b'-> g (b',r), b) -- ------------------- -- Simple API@@ -343,16 +373,16 @@ -- -- > get l = runIdentity . getM l get :: Lens w Identity a b -> a -> b-get l = runIdentity . getM l+get l = runIdentity . liftM snd . runLens l -- | Run the getter function of a pure lens ----- > set l b = runIdentity . setM l b-set :: (a :-> b) -> b -> a -> a-set l b = runIdentity . setM l b+-- > set l b = runIdentity . setM l a+set :: (a :-> b) -> a -> b -> a+set l a = runIdentity . setM l a modify :: (a :-> b) -> (b -> b) -> a -> a-modify l b = runIdentity . modifyM l b+modify l f = runIdentity . modifyM l f -- | a lens that can fail in the Maybe monad on the outer type. Suitable for a -- normal lens on a multi-constructor type. The more general 'setM', 'getM', etc.@@ -360,25 +390,32 @@ type (:~>) = LensM Maybe --- TODO: can we convert an Iso to this kind of type?? Is this more appropriate living in Iso??---- | Set an inner value on an initially 'mempty' value.+-- TODO: get rid of this and instead have a conversion to Iso function+-- rename other to-Iso conversion functions to make some kind of sense+-- lensI :: Monoid a=> Lens a b -> Iso a b+-- lensI l = Iso (getM l) (flip (setM l) mempty)+-- does this make an isomorphism of a well-behaved lens?+-- get l . flip (set l) mempty == id ?+-- get . set mempty OKAY+-- flip (set l) mempty . get l == id ?+-- set mempty $ get a -- only 'id' when 'a' is 'mempty' as well.+-- LAW WE ASSUME HOLDS: set a $ get a+-- and is isoL . lensI == id?+-- CONSIDER...+-- what properties does an Iso have to have for isoL to produce a well-behaved lens?+-- put (Iso _ g) b _ = g b+-- get (Iso f _) a = f a ----- > setZero l b = set l b mzero-setEmpty :: Monoid a => (a :-> b) -> b -> a-setEmpty l b = set l b mempty+-- get . put b = f (g b) -- f and g must be truly isomorphic for resulting lens to be well-behaved+-- put . get = g (f a) +-- put b . put b = g b -- doesn't matter, as put only depends on 'b'+-- OR... +-- rename these below "fill" (since we're "setting" an empty)+-- OR... +-- remove and just use Isos in our example. --- | > setZeroM l b = setM l b mzero-setEmptyM :: (Monoid a, Monad m) => LensM m a b -> b -> m a-setEmptyM l b = setM l b mempty --- | > setEmptyW l b = setW l b mempty-setEmptyW :: (Monoid a, Monad w) => Lens w Identity a b -> b -> w a-setEmptyW l b = setW l b mempty ---TODO: more set variations? getEmpty?-- -- OPERATORS: ------------------------ -- | > (^$) = get@@ -386,5 +423,5 @@ (^$) = get -- | > ma ^>>= l = ma >>= getM l-(^>>=) :: (Monad m)=> m a -> Lens w m a b -> m b+(^>>=) :: (Lenses l m)=> m a -> l m a b -> m b ma ^>>= l = ma >>= getM l
yall.cabal view
@@ -1,6 +1,6 @@ Name: yall -Version: 0.1+Version: 0.2 Synopsis: Lenses with a southern twang @@ -30,9 +30,16 @@ and optionally "Data.Yall.Iso". "Data.Yall" is a simplified, but mostly-compatible, version of a subset of "Data.Yall.Lens". .- TODOs:+ /TODOs/: . - a module providing template haskell deriving of Lenses+ .+ /CHANGES/:+ .+ - remove 'setEmpty' variants only useful on lenses better expressed in Iso+ - create a class for lens operations, supporting...+ - use newtype wrappers for different monadic lifting schemes+ - better ordering for set, allowing let-floating for partial application Homepage: http://brandon.si/code/yall/