functor-combo 0.0.2 → 0.0.3
raw patch · 9 files changed
+889/−129 lines, 9 filesdep +lub
Dependencies added: lub
Files
- functor-combo.cabal +8/−3
- src/FunctorCombo/DHoley.hs +21/−17
- src/FunctorCombo/FixC.hs +10/−4
- src/FunctorCombo/Functor.hs +47/−1
- src/FunctorCombo/Holey.hs +38/−13
- src/FunctorCombo/LocT.hs +6/−4
- src/FunctorCombo/Lub.hs +64/−0
- src/FunctorCombo/MemoTrie.hs +162/−87
- src/FunctorCombo/NonstrictMemo.hs +533/−0
functor-combo.cabal view
@@ -1,5 +1,5 @@ Name: functor-combo-Version: 0.0.2+Version: 0.0.3 Cabal-Version: >= 1.2 Synopsis: Functor combinators with tries & zippers Category: Data@@ -27,16 +27,21 @@ Library hs-Source-Dirs: src Extensions:- Build-Depends: base<5, TypeCompose >= 0.8, containers, data-inttrie+ Build-Depends: base<5, TypeCompose >= 0.8, containers, data-inttrie, lub Exposed-Modules: FunctorCombo.Functor+ FunctorCombo.Lub FunctorCombo.Derivative FunctorCombo.Holey FunctorCombo.DHoley FunctorCombo.FixC FunctorCombo.Regular FunctorCombo.LocT- FunctorCombo.MemoTrie+ FunctorCombo.StrictMemo+ FunctorCombo.NonstrictMemo ghc-options: -Wall++-- FunctorCombo.MemoTrie+ -- ghc-prof-options: -prof -auto-all
src/FunctorCombo/DHoley.hs view
@@ -14,7 +14,7 @@ -- Variation on Holey, integrating 'Der' ---------------------------------------------------------------------- -module FunctorCombo.DHoley (Holey(..)) where+module FunctorCombo.DHoley (Holey(..), fill) where import Control.Arrow (first,second) @@ -28,29 +28,33 @@ -- | Location, i.e., one-hole context and a value for the hole. type Loc f a = (Der f a, a) +-- | Alternative interface for 'fillC'.+fill :: Holey f => Loc f a -> f a+fill = uncurry fillC+ class Functor f => Holey f where -- | Derivative, i.e., one-hole context type Der f :: * -> * -- | Fill a hole- fill :: Loc f a -> f a+ fillC :: Der f a -> a -> f a -- | All extractions extract :: f a -> f (Loc f a) -- The Functor constraint simplifies several signatures below.-instance Holey (Const z) where- type Der (Const z) = Void- fill = error "fill for Const z: no Der values"- extract (Const z) = Const z+instance Holey (Const x) where+ type Der (Const x) = Void+ fillC = voidF+ extract (Const x) = Const x instance Holey Id where type Der Id = Unit- fill (Const (), a) = Id a+ fillC (Const ()) = Id extract (Id a) = Id (Const (), a) instance (Holey f, Holey g) => Holey (f :+: g) where type Der (f :+: g) = Der f :+: Der g- fill (InL df, a) = InL (fill (df, a))- fill (InR df, a) = InR (fill (df, a))+ fillC (InL df) = InL . fillC df+ fillC (InR df) = InR . fillC df extract (InL fa) = InL ((fmap.first) InL (extract fa)) extract (InR ga) = InR ((fmap.first) InR (extract ga)) @@ -62,6 +66,8 @@ extract fa :: f (Loc f a) +extract fa :: f (Der f a, a)+ (fmap.first) InL (extract fa) :: f ((Der f :+: Der g) a, a) (fmap.first) InL (extract fa) :: f ((Der (f :+: g) a), a)@@ -75,8 +81,8 @@ instance (Holey f, Holey g) => Holey (f :*: g) where type Der (f :*: g) = Der f :*: g :+: f :*: Der g- fill (InL (dfa :*: ga), a) = fill (dfa, a) :*: ga- fill (InR ( fa :*: dga), a) = fa :*: fill (dga, a)+ fillC (InL (dfa :*: ga)) = (:*: ga) . fillC dfa+ fillC (InR ( fa :*: dga)) = (fa :*:) . fillC dga extract (fa :*: ga) = (fmap.first) (InL . (:*: ga)) (extract fa) :*: (fmap.first) (InR . (fa :*:)) (extract ga) @@ -155,9 +161,7 @@ instance (Holey f, Holey g) => Holey (g :. f) where type Der (g :. f) = Der g :. f :*: Der f- -- fill (O dgfa :*: dfa) = O . fill dgfa . fill dfa- fill (O dgfa :*: dfa, a) = O (fill (dgfa, fill (dfa, a)))- -- extract (O gfa) = O (extractGF gfa)+ fillC (O dgfa :*: dfa) = O. fillC dgfa . fillC dfa extract = inO extractGF @@ -169,10 +173,10 @@ dgfa :: Der g (f a) dfa :: Der f a -fill dfa a :: f a+fillC dfa a :: f a -fill dgfa (fill dfa a) :: g (f a)+fillC dgfa (fillC dfa a) :: g (f a) -O (fill dgfa (fill dfa a)) :: (g :. f) a+O (fillC dgfa (fillC dfa a)) :: (g :. f) a -}
src/FunctorCombo/FixC.hs view
@@ -14,6 +14,8 @@ module FunctorCombo.FixC (FixC,LocFix, up,down) where +import Control.Arrow (first)+ -- import FunctorCombo.Derivative -- import FunctorCombo.Holey @@ -52,9 +54,9 @@ -- TODO: can I relate FixC to Der (Fix f) and use Loc for LocFix? -up :: Holey f => LocFix f -> LocFix f-up ([],_) = error "up: already at top"-up (d:ds', t) = (ds', Fix (fill (d,t)))+up :: Holey f => LocFix f -> Maybe (LocFix f)+up ([] , _) = Nothing+up (d:ds', t) = Just (ds', Fix (fill (d,t))) {- @@ -77,7 +79,11 @@ -} down :: Holey f => LocFix f -> f (LocFix f)-down (ds', t) = fmap (\ (d,t') -> (d:ds',t')) (extract (unFix t))+down (ds', t) = fmap (first (:ds')) (extract (unFix t))++-- down (ds', t) = fmap (\ (d,t') -> (d:ds',t')) (extract (unFix t))++ {- (ds',t) :: LocFix f
src/FunctorCombo/Functor.hs view
@@ -14,8 +14,9 @@ module FunctorCombo.Functor (- Const(..),Void,Unit,Id(..),unId,inId,inId2,(:+:)(..),eitherF+ Const(..),Void,voidF,Unit,unit,Id(..),unId,inId,inId2,(:+:)(..),eitherF , (:*:)(..),(:.)(..),unO,inO,inO2,(~>)+ , pairF, unPairF, inProd, inProd2 ) where @@ -36,9 +37,17 @@ -- | Empty/zero type constructor (no inhabitants) data Void a +voidF :: Void a -> b+voidF = error "voidF: no value of type Void"++ -- | Unit type constructor (one inhabitant) type Unit = Const () +-- | The unit value+unit :: Unit ()+unit = Const ()+ -- From Control.Compose: -- -- newtype Id a = Id a@@ -65,6 +74,20 @@ instance Functor Void where fmap _ = error "Void fmap: no void value" -- so ghc won't complain +-- deriving instance Functor Void+-- +-- Leads to+-- +-- ghc: panic! (the 'impossible' happened)+-- (GHC version 6.12.1 for i386-apple-darwin):+-- TcPat.checkArgs+-- +-- See ticket <http://hackage.haskell.org/trac/ghc/ticket/4220>.+-- +-- TODO: replace explicit definition with deriving, when the compiler fix+-- has been around for a while.++ -- instance Functor Id where -- fmap h (Id a) = Id (h a) @@ -110,3 +133,26 @@ -- or -- deriving instance (Functor g, Functor f) => Functor (g :. f)+++{--------------------------------------------------------------------+ Some handy structural manipulators+--------------------------------------------------------------------}++pairF :: (f a, g a) -> (f :*: g) a+pairF (fa , ga) = (fa :*: ga)++-- pairF = uncurry (:*:)++unPairF :: (f :*: g) a -> (f a, g a)+unPairF (fa :*: ga) = (fa , ga)++-- Could also define curryF, uncurryF++inProd :: ((f a , g a) -> (h b , i b)) -> ((f :*: g) a -> (h :*: i) b)+inProd = unPairF ~> pairF+++inProd2 :: ((f a , g a) -> (h b , i b) -> (j c , k c))+ -> ((f :*: g) a -> (h :*: i) b -> (j :*: k) c)+inProd2 = unPairF ~> inProd
src/FunctorCombo/Holey.hs view
@@ -12,7 +12,7 @@ -- Filling and extracting derivatives (one-hole contexts) ---------------------------------------------------------------------- -module FunctorCombo.Holey (Loc,Holey(..)) where+module FunctorCombo.Holey (Loc,Holey(..),fill) where import Control.Arrow (first,second) @@ -27,27 +27,43 @@ -- | Location, i.e., one-hole context and a value for the hole. type Loc f a = (Der f a, a) +-- | Alternative interface for 'fillC'.+fill :: Holey f => Loc f a -> f a+fill = uncurry fillC+ -- | Filling and creating one-hole contexts class Functor f => Holey f where- fill :: Loc f a -> f a -- ^ Fill a hole+ fillC :: Der f a -> a -> f a -- ^ Fill a hole extract :: f a -> f (Loc f a) -- ^ All extractions -- The Functor constraint simplifies several signatures below. instance Holey (Const z) where- fill = error "fill for Const z: no Der values"+ fillC = voidF extract (Const z) = Const z instance Holey Id where- fill (Const (), a) = Id a+ fillC (Const ()) = Id extract (Id a) = Id (Const (), a) +-- fillC (Const ()) a = Id a++-- fill (Const (), a) = Id a+ instance (Holey f, Holey g) => Holey (f :+: g) where- fill (InL df, a) = InL (fill (df, a))- fill (InR df, a) = InR (fill (df, a))+ fillC (InL df) = InL . fillC df+ fillC (InR df) = InR . fillC df extract (InL fa) = InL ((fmap.first) InL (extract fa)) extract (InR ga) = InR ((fmap.first) InR (extract ga)) +-- fillC (InL df) a) = InL (fillC df a)+-- fillC (InR df) a) = InR (fillC df a)++-- fill (InL df, a) = InL (fill (df, a))+-- fill (InR df, a) = InR (fill (df, a))++-- fillC = eitherF ((result.result) InL fillC) ((result.result) InR fillC)+ {- InL fa :: (f :+: g) a@@ -68,11 +84,18 @@ -- Der (f :*: g) = Der f :*: g :+: f :*: Der g instance (Holey f, Holey g) => Holey (f :*: g) where- fill (InL (dfa :*: ga), a) = fill (dfa, a) :*: ga- fill (InR ( fa :*: dga), a) = fa :*: fill (dga, a)+ fillC (InL (dfa :*: ga)) = (:*: ga) . fillC dfa+ fillC (InR ( fa :*: dga)) = (fa :*:) . fillC dga extract (fa :*: ga) = (fmap.first) (InL . (:*: ga)) (extract fa) :*: (fmap.first) (InR . (fa :*:)) (extract ga) +-- fillC (InL (dfa :*: ga)) a = fillC dfa a :*: ga+-- fillC (InR ( fa :*: dga)) a = fa :*: fillC dga a++-- fill (InL (dfa :*: ga), a) = fill (dfa, a) :*: ga+-- fill (InR ( fa :*: dga), a) = fa :*: fill (dga, a)++ {- fa :*: ga :: (f :*: g) a@@ -147,12 +170,14 @@ -- Der (g :. f) = Der g :. f :*: Der f instance (Holey f, Holey g) => Holey (g :. f) where- -- fill (O dgfa :*: dfa) = O . fill dgfa . fill dfa- fill (O dgfa :*: dfa, a) = O (fill (dgfa, fill (dfa, a)))+ fillC (O dgfa :*: dfa) = O. fillC dgfa . fillC dfa+ -- fillC (O dgfa :*: dfa) a = O (fillC dgfa (fillC dfa a)) -- extract (O gfa) = O (extractGF gfa) extract = inO extractGF +-- fill (O dgfa :*: dfa, a) = O (fill (dgfa, fill (dfa, a))) + {- O dgfa :*: dfa :: Der (g :. f) a @@ -161,10 +186,10 @@ dgfa :: Der g (f a) dfa :: Der f a -fill dfa a :: f a+fillC dfa a :: f a -fill dgfa (fill dfa a) :: g (f a)+fillC dgfa (fillC dfa a) :: g (f a) -O (fill dgfa (fill dfa a)) :: (g :. f) a+O (fillC dgfa (fillC dfa a)) :: (g :. f) a -}
src/FunctorCombo/LocT.hs view
@@ -18,6 +18,8 @@ ) where +import Control.Arrow (first)+ -- import FunctorCombo.Derivative -- import FunctorCombo.Holey @@ -44,13 +46,13 @@ type LocT t = (Context t, t) -up :: (Regular t, Holey (PF t)) => LocT t -> LocT t-up ([],_) = error "up: already at top"-up (d:ds', t) = (ds', wrap (fill (d,t)))+up :: (Regular t, Holey (PF t)) => LocT t -> Maybe (LocT t)+up ([],_) = Nothing+up (d:ds', t) = Just (ds', wrap (fill (d,t))) down :: (Regular t, Holey (PF t)) => LocT t -> PF t (LocT t)-down (ds', t) = fmap (\ (d,t') -> (d:ds',t')) (extract (unwrap t))+down (ds', t) = fmap (first (:ds')) (extract (unwrap t)) {-
+ src/FunctorCombo/Lub.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE StandaloneDeriving, TypeOperators, FlexibleContexts #-}+{-# OPTIONS_GHC -Wall -fno-warn-orphans #-}+----------------------------------------------------------------------+-- |+-- Module : FunctorCombo.Lub+-- Copyright : (c) Conal Elliott 2010+-- License : GPL-3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Least upper bounds for functor combinators+----------------------------------------------------------------------++module FunctorCombo.Lub (HasLubF) where++import Data.Lub++import FunctorCombo.Functor++-- deriving instance HasLub a => HasLub (Id a)++-- Kills ghc: genDerivBinds: bad derived class lub-0.0.6:Data.Lub.HasLub{tc r3S}++-- ghc: panic! (the 'impossible' happened)+-- (GHC version 6.12.1 for i386-apple-darwin):+-- genDerivBinds: bad derived class lub-0.0.6:Data.Lub.HasLub{tc r3S}++{-++instance HasLub a => HasLub (Id a) where+ lub = inId2 lub++instance (HasLub a, HasLub (f a), HasLub (g a))+ => HasLub ((f :*: g) a) where+ lub = inProd2 lub++-}++{--------------------------------------------------------------------+ Functor-level lubs+--------------------------------------------------------------------}++class HasLubF f where+ lubF :: HasLub v => f v -> f v -> f v++-- instance HasLubF Id where lubF = lub++-- instance (HasLubF f, HasLubF g) => HasLubF (f :*: g) where lubF = lub++-- Could not deduce (HasLub (f v), HasLub (g v)) from the context (HasLub v)+++instance HasLubF Id where lubF = inId2 lub++-- instance (HasLubF f, HasLubF g) => HasLubF (f :*: g) where lubF = inProd2 lub++-- Same error.+++instance (HasLubF f, HasLubF g) => HasLubF (f :*: g) where+ (p :*: q) `lubF` (r :*: s) = (p `lubF` r) :*: (q `lubF` s)++-- TODO: Fix to match lub on pairs. Sublety with strictness.
src/FunctorCombo/MemoTrie.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE TypeOperators, TypeFamilies, UndecidableInstances, CPP #-} {-# OPTIONS_GHC -Wall #-}-{-# OPTIONS_GHC -fno-warn-unused-binds #-} -- temporary while testing+{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-imports #-} -- temporary while testing ---------------------------------------------------------------------- -- | -- Module : FunctorCombo.MemoTrie@@ -11,7 +11,7 @@ -- Maintainer : conal@conal.net -- Stability : experimental -- --- Functor-based memo tries (strict for now)+-- Functor-based memo tries -- ---------------------------------------------------------------------- @@ -20,38 +20,88 @@ HasTrie(..),memo,memo2,memo3 ) where +#define NonstrictMemo+ import Control.Arrow (first) import Control.Applicative ((<$>)) import qualified Data.IntTrie as IT -- data-inttrie import Data.Tree -import Control.Compose (result)+import Control.Compose (result) -- TypeCompose +#ifdef NonstrictMemo+import Data.Lub+#endif+ import FunctorCombo.Functor import FunctorCombo.Regular {--------------------------------------------------------------------+ Misc+--------------------------------------------------------------------}++type Unop a = a -> a++bool :: a -> a -> Bool -> a+bool t e b = if b then t else e+++{-------------------------------------------------------------------- Class --------------------------------------------------------------------} infixr 0 :->: --- | Memo trie from k to v+#ifdef NonstrictMemo+data Trie k v = Trie v (STrie k v)+ type k :->: v = Trie k v +-- Bottom+bottom :: a+bottom = error "MemoTrie: evaluated bottom. Oops!"++-- | Create the trie for the entire domain of a function+trie :: HasLub(v) => HasTrie k => (k -> v) -> (k :->: v)+trie f = Trie (f bottom) (sTrie f)++-- | Convert k trie to k function, i.e., access k field of the trie+untrie :: HasLub(v) => HasTrie k => (k :->: v) -> (k -> v)+untrie (Trie b t) = const b `lub` sUntrie t++#else+type Trie k = STrie k++type k :->: v = k :-> v++-- Bogus HasLub constraint+#define HasLub(v) ()++trie :: HasTrie k => (k -> v) -> (k :->: v)+trie = sTrie++untrie :: HasTrie k => (k :->: v) -> (k -> v)+untrie = sUntrie++#endif++-- | Memo trie from k to v+type k :-> v = STrie k v+ -- | Domain types with associated memo tries class HasTrie k where -- | Representation of trie with domain type @a@- type Trie k :: * -> *+ type STrie k :: * -> * -- | Create the trie for the entire domain of a function- trie :: (k -> v) -> (k :->: v)+ sTrie :: HasLub(v) => (k -> v) -> (k :-> v) -- | Convert k trie to k function, i.e., access k field of the trie- untrie :: (k :->: v) -> (k -> v)- -- | List the trie elements. Order of keys (@:: k@) is always the same.- enumerate :: (k :->: v) -> [(k,v)]+ sUntrie :: HasLub(v) => (k :-> v) -> (k -> v) +-- -- | List the trie elements. Order of keys (@:: k@) is always the same.+-- enumerate :: HasLub(v) => (k :-> v) -> [(k,v)]+ -- -- | Domain elements of a trie -- domain :: HasTrie a => [a] -- domain = map fst (enumerate (trie (const oops)))@@ -59,24 +109,27 @@ -- oops = error "Data.MemoTrie.domain: range element evaluated." +-- TODO: what about enumerate and strict/nonstrict?++ {-------------------------------------------------------------------- Memo functions --------------------------------------------------------------------} -- | Trie-based function memoizer-memo :: HasTrie k => Unop (k -> v)+memo :: HasLub(v) => HasTrie k => Unop (k -> v) memo = untrie . trie -- | Memoize a binary function, on its first argument and then on its -- second. Take care to exploit any partial evaluation.-memo2 :: (HasTrie s,HasTrie t) => Unop (s -> t -> a)+memo2 :: HasLub(a) => (HasTrie s,HasTrie t) => Unop (s -> t -> a) -- | Memoize a ternary function on successive arguments. Take care to -- exploit any partial evaluation.-memo3 :: (HasTrie r,HasTrie s,HasTrie t) => Unop (r -> s -> t -> a)+memo3 :: HasLub(a) => (HasTrie r,HasTrie s,HasTrie t) => Unop (r -> s -> t -> a) -- | Lift a memoizer to work with one more argument.-mup :: HasTrie t => (b -> c) -> (t -> b) -> (t -> c)+mup :: HasLub(c) => HasTrie t => (b -> c) -> (t -> b) -> (t -> c) mup mem f = memo (mem . f) memo2 = mup memo@@ -87,40 +140,55 @@ --------------------------------------------------------------------} instance HasTrie () where- type Trie () = Id- trie f = Id (f ())- untrie (Id v) = const v- enumerate (Id a) = [((),a)]+ type STrie () = Id+ sTrie f = Id (f ())+ sUntrie (Id v) = const v+-- enumerate (Id a) = [((),a)] instance (HasTrie a, HasTrie b) => HasTrie (Either a b) where- type Trie (Either a b) = Trie a :*: Trie b- trie f = trie (f . Left) :*: trie (f . Right)- untrie (ta :*: tb) = untrie ta `either` untrie tb- enumerate (ta :*: tb) = enum' Left ta `weave` enum' Right tb+ type STrie (Either a b) = Trie a :*: Trie b+ sTrie f = trie (f . Left) :*: trie (f . Right)+ sUntrie (ta :*: tb) = untrie ta `either` untrie tb+-- enumerate (ta :*: tb) = enum' Left ta `weave` enum' Right tb -enum' :: (HasTrie a) => (a -> a') -> (a :->: b) -> [(a', b)]-enum' f = (fmap.first) f . enumerate+-- enum' :: HasLub(b) => HasTrie a => (a -> a') -> (a :->: b) -> [(a', b)]+-- enum' f = (fmap.first) f . enumerate -weave :: [a] -> [a] -> [a]-[] `weave` as = as-as `weave` [] = as-(a:as) `weave` bs = a : (bs `weave` as)+-- weave :: [a] -> [a] -> [a]+-- [] `weave` as = as+-- as `weave` [] = as+-- (a:as) `weave` bs = a : (bs `weave` as) +-- To do: rethink enumerate and come back to it. How might enumeration+-- work in the presence of nonstrict memo tries? Maybe lub the+-- approximation into each of the values enumerated from the strict memo tries.+-- Would it help any?? instance (HasTrie a, HasTrie b) => HasTrie (a , b) where- type Trie (a , b) = Trie a :. Trie b- trie f = O (trie (trie . curry f))- untrie (O tt) = uncurry (untrie . untrie tt)- enumerate (O tt) =- [ ((a,b),x) | (a,t) <- enumerate tt , (b,x) <- enumerate t ]+ type STrie (a , b) = Trie a :. Trie b+ sTrie f = O (trie (trie . curry f))+ sUntrie (O tt) = uncurry (untrie . untrie tt)+-- enumerate (O tt) =+-- [ ((a,b),x) | (a,t) <- enumerate tt , (b,x) <- enumerate t ] --- Experiment:+-- Oops:+-- +-- Could not deduce (HasLub (Trie b v)) from the context (HasLub v)+-- arising from a use of `trie'+-- +-- Could not deduce (HasLub (Trie b v)) from the context (HasLub v)+-- arising from a use of `untrie' +-- Eep. How to fix this one?++{-++ #define HasTrieIsomorph(Context,Type,IsoType,toIso,fromIso) \-instance Context => HasTrie (Type) where {\- type Trie (Type) = Trie (IsoType); \- trie f = trie (f . (fromIso)); \- untrie t = untrie t . (toIso); \+instance Context => HasTrie (Type) where { \+ type STrie (Type) = Trie (IsoType); \+ sTrie f = sTrie (f . (fromIso)); \+ sUntrie t = sUntrie t . (toIso); \ enumerate = (result.fmap.first) (fromIso) enumerate; \ } @@ -155,42 +223,25 @@ , (g :. f) a, g (f a) , unO, O ) ----- #define HasTrieRegular(Context,Type) \--- HasTrieIsomorph(Context, Type, PF (Type) (Type) , unwrap, wrap)---- #define HasTrieRegular1(TypeCon) \--- HasTrieRegular(HasTrie a, TypeCon a)---- Hangs ghc 6.12.1:--- --- HasTrieRegular(HasTrie a, [a])--- HasTrieRegular(HasTrie a, Tree a)---- HasTrieRegular1([])--- HasTrieRegular1(Tree)---- I think the problem is infinite types. Try an explicit newtype to--- break the cycle.----- newtype ListTrie a v = ListTrie (PF [a] [a] :->: v)- +-- newtype ListTrie a v = ListTrie (PF [a] [a] :-> v) -- instance HasTrie a => HasTrie [a] where--- type Trie [a] = ListTrie a--- trie f = ListTrie (trie (f . wrap))--- untrie (ListTrie t) = untrie t . unwrap+-- type STrie [a] = ListTrie a+-- sTrie f = ListTrie (trie (f . wrap))+-- sUntrie (ListTrie t) = sUntrie t . unwrap -- enumerate (ListTrie t) = (result.fmap.first) wrap enumerate $ t+-- HasTrieIsomorph( HasTrie (PF ([a]) ([a]) :->: v)+-- , ListTrie a v, PF ([a]) ([a]) :->: v+-- , \ (ListTrie w) -> w, ListTrie ) + -- Works. Now abstract into a macro #define HasTrieRegular(Context,Type,TrieType,TrieCon) \ newtype TrieType v = TrieCon (PF (Type) (Type) :->: v); \ instance Context => HasTrie (Type) where { \- type Trie (Type) = TrieType; \- trie f = TrieCon (trie (f . wrap)); \- untrie (TrieCon t) = untrie t . unwrap; \+ type STrie (Type) = TrieType; \+ sTrie f = TrieCon (sTrie (f . wrap)); \+ sUntrie (TrieCon t) = sUntrie t . unwrap; \ enumerate (TrieCon t) = (result.fmap.first) wrap enumerate t; \ }; \ HasTrieIsomorph( HasTrie (PF (Type) (Type) :->: v) \@@ -199,8 +250,8 @@ -- For instance, --- HasTrieRegular(HasTrie a, [a] , ListTrie a, ListTrie)--- HasTrieRegular(HasTrie a, Tree, TreeTrie a, TreeTrie)+-- HasTrieRegular(HasTrie a, [a] , ListTrie a, ListTrie)+-- HasTrieRegular(HasTrie a, Tree a, TreeTrie a, TreeTrie) -- Simplify a bit with a macro for unary regular types. -- Make similar defs for binary etc as needed.@@ -211,6 +262,7 @@ HasTrieRegular1([] , ListTrie) HasTrieRegular1(Tree, TreeTrie) + -- HasTrieIsomorph(Context,Type,IsoType,toIso,fromIso) -- HasTrieIsomorph( HasTrie (PF [a] [a] :->: v)@@ -218,9 +270,6 @@ -- , \ (ListTrie w) -> w, ListTrie ) --- enumerateEnum :: (Enum k, Num k, HasTrie k) => (k :->: v) -> [(k,v)] enumerateEnum t = [(k, f k) | k <- [0 ..] `weave` [-1, -2 ..]] where@@ -228,9 +277,9 @@ #define HasTrieIntegral(Type) \ instance HasTrie Type where { \- type Trie Type = IT.IntTrie; \- trie = (<$> IT.identity); \- untrie = IT.apply; \+ type STrie Type = IT.IntTrie; \+ sTrie = (<$> IT.identity); \+ sUntrie = IT.apply; \ enumerate = enumerateEnum; \ } @@ -243,15 +292,6 @@ HasTrieIsomorph((HasTrie a, HasTrie (a :->: b)), a -> b, a :->: b, trie, untrie) -{--------------------------------------------------------------------- Misc---------------------------------------------------------------------}--type Unop a = a -> a--bool :: a -> a -> Bool -> a-bool t e b = if b then t else e- {- {--------------------------------------------------------------------@@ -271,8 +311,6 @@ -} -{-- ft1 :: (Bool -> a) -> [a] ft1 f = [f False, f True] @@ -307,10 +345,7 @@ trie1i :: HasTrie a => a :->: a :->: [a] trie1i = unO trie1a --} -{-- ft2 :: ([Bool] -> Int) -> Int ft2 f = f (alts 15) @@ -328,5 +363,45 @@ -- ... (hang forever) ... -- Would nonstrict memoization work? <http://conal.net/blog/posts/nonstrict-memoization/>++f3 :: Bool -> Integer+f3 = const 3++-- *FunctorCombo.MemoTrie> f3 undefined+-- 3+-- *FunctorCombo.MemoTrie> memo f3 undefined+-- *** Exception: Prelude.undefined++f4 :: () -> Integer+f4 = const 4++-- *FunctorCombo.MemoTrie> f4 undefined+-- 4+-- *FunctorCombo.MemoTrie> memo f4 undefined+-- 4++f5 :: ((),()) -> Integer+f5 = const 5++-- *FunctorCombo.MemoTrie> f5 undefined+-- 5+-- *FunctorCombo.MemoTrie> memo f5 undefined+-- 5++f6 :: Either () () -> Integer+f6 = const 6++-- *FunctorCombo.MemoTrie> f6 undefined+-- 6+-- *FunctorCombo.MemoTrie> memo f6 undefined+-- *** Exception: Prelude.undefined++-- Aha!++t6 :: Either () () :-> Integer+t6 = trie f6++-- *FunctorCombo.MemoTrie> t6+-- Id 6 :*: Id 6 -}
+ src/FunctorCombo/NonstrictMemo.hs view
@@ -0,0 +1,533 @@+{-# LANGUAGE TypeOperators, TypeFamilies, UndecidableInstances, CPP+ , DeriveFunctor, StandaloneDeriving+ , FlexibleContexts+ #-}+{-# OPTIONS_GHC -Wall #-}+{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-imports #-} -- temporary while testing+----------------------------------------------------------------------+-- |+-- Module : FunctorCombo.NonstrictMemo+-- Copyright : (c) Conal Elliott 2010+-- License : BSD3+-- +-- Maintainer : conal@conal.net+-- Stability : experimental+-- +-- Functor-based memo tries+-- +----------------------------------------------------------------------++module FunctorCombo.NonstrictMemo+ (+ HasTrie(..),memo,memo2,memo3+ ) where++#define NonstrictMemo++import Control.Arrow (first)+import Control.Applicative ((<$>))++import qualified Data.IntTrie as IT -- data-inttrie+import Data.Tree++import Control.Compose (result) -- TypeCompose++#ifdef NonstrictMemo+import Data.Lub+#endif++import FunctorCombo.Functor+import FunctorCombo.Regular+++{--------------------------------------------------------------------+ Misc+--------------------------------------------------------------------}++type Unop a = a -> a++bool :: a -> a -> Bool -> a+bool t e b = if b then t else e+++{--------------------------------------------------------------------+ Class+--------------------------------------------------------------------}++infixr 0 :->:++#define FunctorSuperClass++#ifdef FunctorSuperClass++#define HasTrieContext(Ty) Functor (STrie(Ty))+#define HF(Ty) HasTrie (Ty)++#else+#define HasTrieContext(Ty) ()+#define HF(Ty) HasTrie (Ty), Functor (STrie (Ty))++#endif+++#ifdef NonstrictMemo+data Trie k v = Trie v (STrie k v)++deriving instance Functor (STrie k) => Functor (Trie k)++-- instance Functor (STrie k) => Functor (Trie k) where+-- fmap f (Trie b t) = Trie (f b) (fmap f t)++type k :->: v = Trie k v++-- Bottom+bottom :: a+bottom = error "MemoTrie: evaluated bottom. Oops!"++-- | Create the trie for the entire domain of a function+trie :: (HasTrie k{-, HasLub v-}) => (k -> v) -> (k :->: v)+trie f = Trie (f bottom) (sTrie f)++-- | Convert k trie to k function, i.e., access k field of the trie+untrie :: (HasTrie k, HasLub v) => (k :->: v) -> (k -> v)+untrie (Trie b t) = const b `lub` sUntrie t++#else+type Trie k = STrie k++type k :->: v = k :-> v++-- Bogus HasLub constraint+#define HasLub(v) ()++trie :: HasTrie k => (k -> v) -> (k :->: v)+trie = sTrie++untrie :: HasTrie k => (k :->: v) -> (k -> v)+untrie = sUntrie++#endif++-- | Strict memo trie from k to v+type k :-> v = STrie k v++-- | Domain types with associated memo tries+class HasTrieContext(k) => HasTrie k where+ -- | Representation of trie with domain type @a@+ type STrie k :: * -> *+ -- | Create the trie for the entire domain of a function+ sTrie :: {- HasLub(v) => -} (k -> v) -> (k :-> v)+ -- | Convert k trie to k function, i.e., access k field of the trie+ sUntrie :: HasLub(v) => (k :-> v) -> (k -> v)++-- -- | List the trie elements. Order of keys (@:: k@) is always the same.+-- enumerate :: HasLub(v) => (k :-> v) -> [(k,v)]++-- -- | Domain elements of a trie+-- domain :: HasTrie a => [a]+-- domain = map fst (enumerate (trie (const oops)))+-- where+-- oops = error "Data.MemoTrie.domain: range element evaluated."+++-- TODO: what about enumerate and strict/nonstrict?+++{--------------------------------------------------------------------+ Memo functions+--------------------------------------------------------------------}++-- | Trie-based function memoizer+memo :: HasLub(v) => HasTrie k => Unop (k -> v)+memo = untrie . trie++-- | Memoize a binary function, on its first argument and then on its+-- second. Take care to exploit any partial evaluation.+memo2 :: HasLub(a) => (HasTrie s,HasTrie t) => Unop (s -> t -> a)++-- | Memoize a ternary function on successive arguments. Take care to+-- exploit any partial evaluation.+memo3 :: HasLub(a) => (HasTrie r,HasTrie s,HasTrie t) => Unop (r -> s -> t -> a)++-- | Lift a memoizer to work with one more argument.+mup :: HasLub(c) => HasTrie t => (b -> c) -> (t -> b) -> (t -> c)+mup mem f = memo (mem . f)++memo2 = mup memo+memo3 = mup memo2+++{--------------------------------------------------------------------+ Instances+--------------------------------------------------------------------}++instance HasTrie () where+ type STrie () = Id+ sTrie f = Id (f ())+ sUntrie (Id v) = \ () -> v+-- enumerate (Id a) = [((),a)]++instance (HasTrie a, HasTrie b) => HasTrie (Either a b) where+ type STrie (Either a b) = Trie a :*: Trie b+ sTrie f = trie (f . Left) :*: trie (f . Right)+ sUntrie (ta :*: tb) = untrie ta `either` untrie tb+-- enumerate (ta :*: tb) = enum' Left ta `weave` enum' Right tb++-- enum' :: HasLub(b) => HasTrie a => (a -> a') -> (a :->: b) -> [(a', b)]+-- enum' f = (fmap.first) f . enumerate++-- weave :: [a] -> [a] -> [a]+-- [] `weave` as = as+-- as `weave` [] = as+-- (a:as) `weave` bs = a : (bs `weave` as)++-- To do: rethink enumerate and come back to it. How might enumeration+-- work in the presence of nonstrict memo tries? Maybe lub the+-- approximation into each of the values enumerated from the strict memo tries.+-- Would it help any??++instance (HF(a), HasTrie b) => HasTrie (a , b) where+ type STrie (a , b) = Trie a :. Trie b+-- sTrie f = O (trie (trie . curry f))+-- sUntrie (O tt) = uncurry (untrie . untrie tt)+ sTrie f = O (fmap trie (trie (curry f)))+ sUntrie (O tt) = uncurry (untrie (fmap untrie tt))++-- enumerate (O tt) =+-- [ ((a,b),x) | (a,t) <- enumerate tt , (b,x) <- enumerate t ]++-- With the first definitions of sTrie and sUntrie:+-- +-- Could not deduce (HasLub (Trie b v)) from the context (HasLub v)+-- arising from a use of `trie'+-- +-- Could not deduce (HasLub (Trie b v)) from the context (HasLub v)+-- arising from a use of `untrie'+-- +-- Solution: switch from inner-then-outer to outer-then-inner.++#define HasTrieIsomorph(Context,Type,IsoType,toIso,fromIso) \+instance Context => HasTrie (Type) where { \+ type STrie (Type) = STrie (IsoType); \+ sTrie f = sTrie (f . (fromIso)); \+ sUntrie t = sUntrie t . (toIso); \+}++ -- enumerate = (result.fmap.first) (fromIso) enumerate; \+++HasTrieIsomorph( (), Bool, Either () ()+ , bool (Left ()) (Right ())+ , either (\ () -> True) (\ () -> False))++HasTrieIsomorph( (HF(a),HF(b), HasTrie c)+ , (a,b,c), ((a,b),c)+ , \ (a,b,c) -> ((a,b),c), \ ((a,b),c) -> (a,b,c))++HasTrieIsomorph( (HF(a),HF(b),HF(c), HasTrie d)+ , (a,b,c,d), ((a,b,c),d)+ , \ (a,b,c,d) -> ((a,b,c),d), \ ((a,b,c),d) -> (a,b,c,d))+++-- As well as the functor combinators themselves++HasTrieIsomorph( HasTrie x, Const x a, x, getConst, Const )++HasTrieIsomorph( HasTrie a, Id a, a, unId, Id )++HasTrieIsomorph( ( HF(f a), HasTrie (g a) )+ , (f :*: g) a, (f a,g a)+ , \ (fa :*: ga) -> (fa,ga), \ (fa,ga) -> (fa :*: ga) )++HasTrieIsomorph( (HasTrie (f a), HasTrie (g a))+ , (f :+: g) a, Either (f a) (g a)+ , eitherF Left Right, either InL InR )++HasTrieIsomorph( HasTrie (g (f a))+ , (g :. f) a, g (f a) , unO, O )+++newtype ListSTrie a v = ListSTrie (PF [a] [a] :-> v)+ +deriving instance Functor (STrie a) => Functor (ListSTrie a)+ +instance (HF(a)) => HasTrie [a] where+ type STrie [a] = ListSTrie a+ sTrie f = ListSTrie (sTrie (f . wrap))+ sUntrie (ListSTrie t) = sUntrie t . unwrap+ -- enumerate (ListSTrie t) = (result.fmap.first) wrap enumerate $ t++-- Compiles fine, but has many points of undefinedness than a list does.+-- Experiment with some alternatives.++++-- Now a trie for ListSTrie a v. Use the isomorphism with PF [a] [a] :-> v++-- HasTrieIsomorph( HasTrie (PF ([a]) ([a]) :->: v)+-- , ListSTrie a v, PF ([a]) ([a]) :->: v+-- , \ (ListSTrie w) -> w, ListSTrie )+++-- instance HasTrie (PF ([a]) ([a]) :->: v) => HasTrie (ListSTrie a v) where+-- type STrie (ListSTrie a v) = STrie (PF ([a]) ([a]) :->: v)+-- sTrie f = sTrie (f . ListSTrie)+-- -- sUntrie t = sUntrie t . (\ (ListSTrie w) -> w)+++-- Could not deduce (HasTrie (Trie ((:*:) (Const a) Id [a]) v),+-- Functor (STrie (Trie (Const () [a]) v)),+-- HasTrie (Trie (Const () [a]) v))+-- from the context (HasLub v1)+-- arising from a use of `sTrie'++-- Couldn't match expected type `STrie+-- (Trie ((:+:) Unit (Const a :*: Id) [a]) v)'+-- against inferred type `Trie (Trie (Const () [a]) v)+-- :. Trie (Trie ((:*:) (Const a) Id [a]) v)'+-- NB: `STrie' is a type function, and may not be injective+-- In the expression: sTrie (f . ListSTrie)++{-++f :: ListSTrie a v -> u++ListSTrie :: (PF [a] [a] :-> v) -> ListSTrie a v++f . ListSTrie :: (PF [a] [a] :-> v) -> u++sTrie (f . ListSTrie) :: HasTrie (PF [a] [a] :-> v) => (PF [a] [a] :-> v) :-> u+ :: HasTrie (PF [a] [a] :-> v) => ListSTrie a v :-> u++ListSTrie a v :-> u++(PF [a] [a] :-> v) :-> u++((Unit :+: Const a :*: Id) [a] :-> v) :-> u++(Either (Unit [a]) ((Const a :*: Id) [a]) :-> v) :-> u++STrie (Either (Unit [a]) ((Const a :*: Id) [a])) v :-> u++(Trie (Unit [a]) :*: Trie ((Const a :*: Id) [a])) v :-> u++(Trie (Unit [a]) v , Trie ((Const a :*: Id) [a]) v) :-> u++(Unit [a] :->: v , (Const a :*: Id) [a] :->: v) :-> u++STrie (Unit [a] :->: v , (Const a :*: Id) [a] :->: v) u++(Trie (Unit [a] :->: v) :. Trie ((Const a :*: Id) [a] :->: v)) u++(Trie (Unit [a] :->: v) :. Trie ((Const a [a], Id [a]) :->: v)) u++(Trie (Unit [a] :->: v) :. Trie ((Const a [a], Id [a]) :->: v)) u+++(v , (Const a [a], Id [a]) :-> v)++(v , Const a [a] :->: Id [a] :->: v)++(v , (Id [a] :->: v, Const a [a] :-> Id [a] :->: v))++(v , (Id [a] :->: v, Const a [a] :-> (v, Id [a] :-> v)))++(v , ((v, Id [a] :-> v), Const a [a] :-> (v, Id [a] :-> v)))++(v , ((v, [a] :->: v), a :->: (v, [a] :->: v)))++++ListSTrie a v++PF [a] [a] :-> v++(Unit :+: Const a :*: Id) [a] :-> v++Either (Unit [a]) ((Const a :*: Id) [a]) :-> v++STrie (Either (Unit [a]) ((Const a :*: Id) [a])) v++(Trie (Unit [a]) :*: Trie ((Const a :*: Id) [a])) v+++-}++++{-++-- Now abstract into a macro++#define HasTrieRegular(Context,Type,TrieType,TrieCon) \+newtype TrieType v = TrieCon (PF (Type) (Type) :->: v); \+instance Context => HasTrie (Type) where { \+ type STrie (Type) = TrieType; \+ sTrie f = TrieCon (sTrie (f . wrap)); \+ sUntrie (TrieCon t) = sUntrie t . unwrap; \+}; \+HasTrieIsomorph( HasTrie (PF (Type) (Type) :->: v) \+ , TrieType v, PF (Type) (Type) :->: v \+ , \ (TrieCon w) -> w, TrieCon )++-- enumerate (TrieCon t) = (result.fmap.first) wrap enumerate t; \++++-- For instance,++-- HasTrieRegular(HasTrie a, [a] , ListSTrie a, ListSTrie)+-- HasTrieRegular(HasTrie a, Tree a, TreeTrie a, TreeTrie)++-- Simplify a bit with a macro for unary regular types.+-- Make similar defs for binary etc as needed.++#define HasTrieRegular1(TypeCon,TrieCon) \+HasTrieRegular(HasTrie a, TypeCon a, TrieCon a, TrieCon)++-- HasTrieRegular1([] , ListSTrie)+-- HasTrieRegular1(Tree, TreeTrie)+++-- HasTrieIsomorph(Context,Type,IsoType,toIso,fromIso)++-- HasTrieIsomorph( HasTrie (PF [a] [a] :->: v)+-- , ListSTrie a v, PF [a] [a] :->: v+-- , \ (ListSTrie w) -> w, ListSTrie )+++enumerateEnum :: (Enum k, Num k, HasTrie k) => (k :->: v) -> [(k,v)]+enumerateEnum t = [(k, f k) | k <- [0 ..] `weave` [-1, -2 ..]]+ where+ f = untrie t++#define HasTrieIntegral(Type) \+instance HasTrie Type where { \+ type STrie Type = IT.IntTrie; \+ sTrie = (<$> IT.identity); \+ sUntrie = IT.apply; \+ enumerate = enumerateEnum; \+}++HasTrieIntegral(Int)+HasTrieIntegral(Integer)+++-- Memoizing higher-order functions++HasTrieIsomorph((HasTrie a, HasTrie (a :->: b)), a -> b, a :->: b, trie, untrie)+++{-++{--------------------------------------------------------------------+ Testing+--------------------------------------------------------------------}++fib :: Integer -> Integer+fib m = mfib m+ where+ mfib = memo fib'+ fib' 0 = 0+ fib' 1 = 1+ fib' n = mfib (n-1) + mfib (n-2)++-- The eta-redex in fib is important to prevent a CAF.+++-}++ft1 :: (Bool -> a) -> [a]+ft1 f = [f False, f True]++f1 :: Bool -> Int+f1 False = 0+f1 True = 1++trie1a :: HasTrie a => (Bool -> a) :->: [a]+trie1a = trie ft1++trie1b :: HasTrie a => (Bool :->: a) :->: [a]+trie1b = trie1a++trie1c :: HasTrie a => (Either () () :->: a) :->: [a]+trie1c = trie1a++trie1d :: HasTrie a => ((Trie () :*: Trie ()) a) :->: [a]+trie1d = trie1a++trie1e :: HasTrie a => (Trie () a, Trie () a) :->: [a]+trie1e = trie1a++trie1f :: HasTrie a => (() :->: a, () :->: a) :->: [a]+trie1f = trie1a++trie1g :: HasTrie a => (a, a) :->: [a]+trie1g = trie1a++trie1h :: HasTrie a => (Trie a :. Trie a) [a]+trie1h = trie1a++trie1i :: HasTrie a => a :->: a :->: [a]+trie1i = unO trie1a+++ft2 :: ([Bool] -> Int) -> Int+ft2 f = f (alts 15)++alts :: Int -> [Bool]+alts n = take n (cycle [True,False])++f2 :: [Bool] -> Int+f2 = length . filter id++-- Memoization fails:++-- *FunctorCombo.MemoTrie> ft2 f2+-- 8+-- *FunctorCombo.MemoTrie> memo ft2 f2+-- ... (hang forever) ...++-- Would nonstrict memoization work? <http://conal.net/blog/posts/nonstrict-memoization/>++f3 :: Bool -> Integer+f3 = const 3++-- *FunctorCombo.MemoTrie> f3 undefined+-- 3+-- *FunctorCombo.MemoTrie> memo f3 undefined+-- *** Exception: Prelude.undefined++f4 :: () -> Integer+f4 = const 4++-- *FunctorCombo.MemoTrie> f4 undefined+-- 4+-- *FunctorCombo.MemoTrie> memo f4 undefined+-- 4++f5 :: ((),()) -> Integer+f5 = const 5++-- *FunctorCombo.MemoTrie> f5 undefined+-- 5+-- *FunctorCombo.MemoTrie> memo f5 undefined+-- 5++f6 :: Either () () -> Integer+f6 = const 6++-- *FunctorCombo.MemoTrie> f6 undefined+-- 6+-- *FunctorCombo.MemoTrie> memo f6 undefined+-- *** Exception: Prelude.undefined++-- Aha!++t6 :: Either () () :-> Integer+t6 = trie f6++-- *FunctorCombo.MemoTrie> t6+-- Id 6 :*: Id 6++-}