pez (empty) → 0.0.1
raw patch · 7 files changed
+698/−0 lines, 7 filesdep +basedep +fclabelsdep +thristsetup-changed
Dependencies added: base, fclabels, thrist
Files
- Data/Record/Label/Prelude.hs +22/−0
- Data/Typeable/Zipper.hs +293/−0
- EXAMPLES/Examples.hs +136/−0
- LICENSE +30/−0
- Setup.hs +3/−0
- Tests.hs +121/−0
- pez.cabal +93/−0
+ Data/Record/Label/Prelude.hs view
@@ -0,0 +1,22 @@+{-# LANGUAGE TypeOperators #-}+module Data.Record.Label.Prelude+ where++import Data.Record.Label+++-- First class labels pre-defined for the standard types from haskell's prelude++-- | [a]+lHead :: [a] :-> a+lHead = lens head (:)++lTail :: [a] :-> [a]+lTail = lens tail (\t-> (:t) . head)++-- | (a,b)+lFst :: (a,b) :-> a+lFst = lens fst (\a (_,b)-> (a,b))++lSnd :: (a,b) :-> b+lSnd = lens snd (\b (a,_)-> (a,b))
+ Data/Typeable/Zipper.hs view
@@ -0,0 +1,293 @@+{-# LANGUAGE GeneralizedNewtypeDeriving, TypeOperators, TemplateHaskell, +GADTs, DeriveDataTypeable #-}+module Data.Typeable.Zipper (++ -- * Basic Zipper functionality+ Zipper() + -- ** Creating and closing Zippers+ , zipper , close+ -- ** Moving around+ , ZPath(..) , moveUp+ -- ** Querying+ , focus , viewf , atTop ++ -- * Advanced functionality+ -- ** Saving positions in a Zipper+ , SavedPath + , save + , saveFromAbove+ , savedLens + , closeSaving+ , moveUpSaving+ -- ** Recalling positions:+ , restore ++ -- * Convenience operators, types, and exports+ , Zipper1+ -- ** Operators+ , (.+) , (.>) , (.-) , (?+) , (?>) , (?-)+ -- ** Export Typeable class and fclabels package+ , module Data.Record.Label+ , Data.Typeable.Typeable +) where++{- + - DESCRIPTION:+ -+ - we use a Thrist to create a type-threaded stack of continuations+ - allowing us to have a polymorphic history of where we've been.+ - by using Typeable, we are able to "move up" by type casting in+ - the Maybe monad. This means that the programmer has to know+ - what type a move up will produce, or deal with unknowns.+ -+ -+ - TODO NOTES+ -+ - - Include as part of package a module: Data.Record.Label.Prelude that+ - exports labels for haskell builtin types+ -+ - - Create a 'moveUntil' function, or something else to capture the ugly:+ - descend z@(viewf -> Gong) = z+ - descend z = descend $ moveTo tock z+ - ...perhaps we can make something clever using property of pattern match+ - failure in 'do' block?+ -+ - - When the 'fclabels' package supports failure handling a.la the code on+ - Github, then these functions will take advantage of that by returning+ - Nothing when a lens is applied to an invalid constructor:+ - * moveTo+ - * restore+ - + - - consider instead of using section, use head form of parent with+ - the child node set to undefined. Any performance difference?+ -+ - - actually look at how this performs in terms of space/time+ -+ - ROADMAP:+ - Pink Elephant+ - Patiently Expectant+ - Probably ??+ -+ -}++ -- this is where the magic happens:+import Data.Record.Label+import Data.Typeable+import Data.Thrist++ -- for our accessors, which are a category:+import Control.Category +import Prelude hiding ((.), id) -- take these from Control.Category+import Control.Applicative+++ -------------------------+ -- TYPES: the real heros+ ------------------------+++ -- We store our history in a type-threaded list of pairs of lenses and+ -- continuations (parent data-types with a "hole" where the child fits):+ -- Use GADT to enforce Typeable constraint+data HistPair b a where + H :: (Typeable a, Typeable b)=> { hLens :: (a :-> b),+ hCont :: (b -> a) } -> HistPair b a++type ZipperStack b a = Thrist HistPair b a++data Zipper a b = Z { stack :: ZipperStack b a,+ _focus :: b + } deriving (Typeable)+ ++-- | stores the path used to return to the same location in a data structure+-- as the one we just exited. You can also extract a lens from a SavedPath that+-- points to that location:+newtype SavedPath a b = S { savedLenses :: Thrist TypeableLens a b } + deriving (Typeable, Category)++-- We need another GADT here to enforce the Typeable constraint within the+-- hidden types in our thrist of lenses above:+data TypeableLens a b where+ TL :: (Typeable a,Typeable b)=> {tLens :: (a :-> b)} -> TypeableLens a b++++-- TODO: TRY USING FUNDEPS ALA THE MONAD TRANSFORMER LIBRARIES FOR CLASS+-- CONSTRAINTS HERE:+--class (Typeable b, Typeable c) => ZPath p b c | p -> b, p -> c where+--+-- | Types of the ZPath class act as references to "paths" down through a datatype.+-- Currently lenses from 'fclabels' and SavedPath types are instances+class ZPath p where+ -- | Move down the structure to the label specified. Return Nothing if the+ -- label is not valid for the focus's constructor:+ moveTo :: (Typeable b, Typeable c) => p b c -> Zipper a b -> Zipper a c++++ ---------------------------+ -- Basic Zipper Functions:+ ---------------------------+++-- | a fclabel lens for setting, getting, and modifying the zipper's focus:+$(mkLabelsNoTypes [''Zipper])+++instance ZPath (:->) where+ moveTo = flip pivot . TL++instance ZPath SavedPath where+ moveTo = flip (foldlThrist pivot) . savedLenses +++-- | Move up n levels as long as the type of the parent is what the programmer+-- is expecting and we aren't already at the top. Otherwise return Nothing.+moveUp :: (Typeable c, Typeable b)=> Int -> Zipper a c -> Maybe (Zipper a b)+moveUp 0 z = gcast z+moveUp n (Z (Cons (H _ f) stck) c) = moveUp (n-1) (Z stck $ f c)+moveUp _ _ = Nothing +++zipper :: a -> Zipper a a+zipper = Z Nil+++close :: Zipper a b -> a+close = snd . closeSaving++++ ------------------------------+ -- ADVANCED ZIPPER FUNCTIONS:+ ------------------------------++--- THIS FUNCTION GAVE ME THE MOST TROUBLE AND COULD PROBABLY BE SIMPLIFIED AND+--- 'moveUP' DEFINED IN TERMS OF IT, BUT FOR NOW I AM HAPPY WITH SOMETHING THAT+--- WORKS. ++-- | Move up a level as long as the type of the parent is what the programmer+-- is expecting and we aren't already at the top. Otherwise return Nothing.+moveUpSaving :: (Typeable c, Typeable b)=> Int -> Zipper a c -> Maybe (Zipper a b, SavedPath b c)+moveUpSaving n z = (,) <$> moveUp n z <*> saveFromAbove n z++data ZipperLenses a c b = ZL { zlStack :: ZipperStack b a,+ zLenses :: Thrist TypeableLens b c }+++-- | return a SavedPath from n levels up to the current level+saveFromAbove n = fmap (S . zLenses) . mvUpSavingL n . flip ZL Nil . stack+ where+ mvUpSavingL :: (Typeable b', Typeable b)=> Int -> ZipperLenses a c b -> Maybe (ZipperLenses a c b')+ mvUpSavingL 0 z = gcast z+ mvUpSavingL n (ZL (Cons (H l _) stck) ls) = mvUpSavingL (n-1) (ZL stck $ Cons (TL l) ls)+ mvUpSavingL _ _ = Nothing++++closeSaving :: Zipper a b -> (SavedPath a b, a)+closeSaving (Z stck b) = (S ls, a)+ where ls = getReverseLensStack stck+ a = compStack (mapThrist hCont stck) b+++-- | Return a SavedPath type encapsulating the current location in the Zipper.+-- This lets you return to a location in your data type after closing the +-- Zipper.+save :: Zipper a b -> SavedPath a b+save = fst . closeSaving++-- | Extract a composed lens that points to the location we SavedPath. This lets +-- us modify, set or get a location that we visited with our Zipper after +-- closing the Zipper.+savedLens :: (Typeable a, Typeable b)=> SavedPath a b -> (a :-> b)+savedLens = compStack . mapThrist tLens . savedLenses+++-- | Return to a previously SavedPath location within a data-structure. +-- Saving and restoring lets us for example: find some location within our +-- structure using a Zipper, save the location, fmap over the entire structure,+-- and then return to where we were:+restore :: (ZPath p, Typeable a, Typeable b)=> p a b -> a -> Zipper a b+restore s = moveTo s . zipper+++-- | returns True if Zipper is at the top level of the data structure:+atTop :: Zipper a b -> Bool+atTop = nullThrist . stack++{-+-- | Return our depth in the Zipper. if atTop z then level z == 0+level :: Zipper a b -> Int+level = foldlThrist (.) ...forgot how to do this :(+-}+----------------------------------------------------------------------------+++ ----------------+ -- CONVENIENCE+ ----------------++-- | a view function for a Zipper's focus. Defined simply as: `getL` focus+viewf :: Zipper a b -> b+viewf = getL focus++-- | a simple type synonym for a Zipper where the type at the focus is the+-- same as the type of the outer (unzippered) type. Cleans up type signatures+-- for simple recursive types:+type Zipper1 a = Zipper a a+++-- bind higher than <$>. Is this acceptable?:+infixl 5 .+, .>, .-, ?+, ?>, ?-++-- | 'moveTo' with arguments flipped. Operator plays on the idea of addition of+-- levels onto the focus.+(.+) :: (ZPath p, Typeable b, Typeable c)=> Zipper a b -> p b c -> Zipper a c+(.+) = flip moveTo++-- | 'moveUp' with arguments flipped. Operator syntax comes from the idea of+-- moving up as subtraction.+(.-) :: (Typeable c, Typeable b)=> Zipper a c -> Int -> Maybe (Zipper a b)+(.-) = flip moveUp++-- | setL focus, with arguments flipped+(.>) :: Zipper a b -> b -> Zipper a b+(.>) = flip (setL focus)++(?+) :: (ZPath p, Typeable b, Typeable c)=> Maybe (Zipper a b) -> p b c -> Maybe (Zipper a c)+(?+)= flip (fmap . moveTo)++(?-) :: (Typeable c, Typeable b)=> Maybe (Zipper a c) -> Int -> Maybe (Zipper a b)+mz ?- n = mz >>= moveUp n++(?>) :: Maybe (Zipper a b) -> b -> Maybe (Zipper a b)+(?>) = flip (fmap . setL focus)+++ ------------+ -- HELPERS+ ------------++ -- The core of our 'moveTo' function+pivot (Z t a') (TL l) = Z (Cons h t) b+ where h = H l (a' `missing` l)+ b = getL l a'+ --TODO: MAYBE GIVE THE GC SOME STRICTNESS HINTS HERE?:+ missing a l = flip (setL l) a+++ -- fold a thrist into a single category by composing the stack with (.)+ -- Here 'cat' will be either (->) or (:->):+compStack :: (Category cat)=> Thrist cat b a -> cat b a+compStack = foldrThrist (flip(.)) id+++ -- Takes the zipper stack and extracts each lens segment, and recomposes+ -- them in reversed order, forming a lens from top to bottom of a data + -- structure:+getReverseLensStack :: ZipperStack b a -> Thrist TypeableLens a b+getReverseLensStack = unflip . foldlThrist rev (Flipped Nil)+ where rev (Flipped t) (H l _) = Flipped $ Cons (TL l) t+
+ EXAMPLES/Examples.hs view
@@ -0,0 +1,136 @@+> {-# LANGUAGE TemplateHaskell, DeriveDataTypeable, TypeOperators, ViewPatterns #-}++The first three extensions above are almost always required when using 'pez':+ - TemplateHaskell for generating lenses via Data.Record.Label+ - TypeOperators for infix (:->) from 'fclabels' package+ - DeriveDataTypeable for deriving Typeable on user-defined types++We also use ViewPatterns which are useful for pattern matching on our zipper's+focus.++> module Main+> where++ Import the 'pez' library (which also brings in Data.Record.Label and+Data.Typeable:++> import Data.Typeable.Zipper+> import Control.Applicative+++ ------------------------------------+ EXAMPLE 1: + A binary tree+ ------------------------------------+++ We define a simple binary search tree, deriving its Typeable instance.+Typeable "reify"s the type of some data, basically bringing some of the +type system into the world of data.+ Further, we create accessor functions starting with an underdash. This+will let the 'fclabels' package generate lenses for our tree. See below.++> data Tree a = Node { _leftNode :: Tree a, +> _val :: a, +> _rightNode :: Tree a }+> | Nil +> deriving (Typeable,Show)+ ++ Now we use some templete haskell provided by 'fclabels' to generate our+lenses. We use these lenses to refer to children nodes we would like to move+to.+ The code below will automatically create lenses named "leftNode", +"rightNode", and "val" at compile time. You can see their types in ghci.++> $(mkLabelsNoTypes [''Tree])+++At this point we have everything we need to work with `Tree` in a Zipper! Let's +try it out on an example `Tree` that looks like...++ b+ / \+ a c++> tree = Node (Node Nil 'a' Nil) 'b' (Node Nil 'c' Nil)++Let's use our zipper to apply a clockwise rotation (a rebalancing procedure) +on the leftmost node, which in the case of the tree above would produce...++ a+ \+ b+ \+ c+++> rotateLeftmost :: Tree Char -> Maybe (Tree Char)+> rotateLeftmost = fmap close . (doRotation =<<) . moveUp 1 . descend . zipper+> -- travel down the left side of the tree, until reaching a Nil branch:+> where descend z@(viewf-> Nil) = z+> descend z = descend $ moveTo leftNode z+>+> -- use the Zipper1 type synonym for brevity when outer constructor+> -- is the same as the focus:+> doRotation :: Zipper1 (Tree Char) -> Maybe (Zipper1 (Tree Char))+> doRotation z1@(viewf->Node l1 a1 r1) = do+> -- navigate up one level in the zipper:+> z0 <- moveUp 1 $ setL focus Nil z1+> -- perform clockwise rotation:+> let (Node _ a0 r0) = viewf z0+> z0' = setL focus (Node l1 a1 $ Node r1 a0 r0) z0+> return z0'+++ ------------------------------------+ EXAMPLE 1b: + Monadic interface+ ------------------------------------++ The code above would be a little less clunky if we used a State monad.+Specifically, we will use the State / Maybe monad transformer, and see how+the code above looks:++... > type ZipperState a = StateT (Zipper1 (Tree Char)) Maybe a+...todo when we finish the monadic interface+++ ------------------------------------+ EXAMPLE 2+ Mutually-recursive types+ ------------------------------------++Typeable allows us to define 'moveUp' on mutually-recursive data types, when we+wouldn't otherwise be able to make such a function type-check. It falls on the+module user to make sure that a 'moveUp' will land us at the type we were+expecting. Here is an example:++> newtype Timer = Timer { tickTocks :: Tick } deriving Show+>+> data Tick = Tick { _tock :: Tock }+> | Claaaannnnggg deriving (Show, Typeable)+>+> data Tock = Tock { _tick :: Tick } deriving (Show, Typeable)+>+> timer = Timer $ Tick $ Tock $ Tick $ Tock $ Claaaannnnggg++Once again we will generate the labels for the types we will pass through with+our zipper:++> $(mkLabelsNoTypes [''Tick, ''Tock])+++Let's make a function that shortens the timer by one tick-tock pair. We'll also+demonstrate some of the convenience operators for moving and setting the focus,+these may change or disappear if I decide they are a bad idea:++> shortenTimer :: Timer -> Maybe Timer+> shortenTimer = fmap (Timer . close) . shortenTick . zipper . tickTocks+> where shortenTick z@(viewf-> Claaaannnnggg) = +> z .- 2 ?> Claaaannnnggg+> shortenTick z = shortenTick (z .+ tock .+ tick)++The function above would have returned Nothing from 'moveUp' had the timer not +had at least one Tick-Tock pair, OR should we have arrived by moving up at a+type we were not expecting.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Brandon Simmons 2011++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Brandon Simmons nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ Tests.hs view
@@ -0,0 +1,121 @@+{-# LANGUAGE DeriveDataTypeable, TemplateHaskell, ViewPatterns #-}+module Main+ where++import Test.QuickCheck+import Data.Typeable.Zipper+import Data.Record.Label.Prelude++{-+ - These tests are vital, since with all the dynamic magic we're using, a+ - function that compiles could very well not actually work+ -}++-- a linear, mutually recursive type:+data Tick = Tick { _tock :: Tock }+ | Gong + deriving (Typeable, Eq, Show)++data Tock = LoudTock { _tick :: Tick }+ | SoftTock { _tick :: Tick }+ deriving (Typeable, Eq, Show)++newtype TickTock = TT { _tickTocks :: Tick }+ deriving (Typeable, Eq, Show)++$(mkLabelsNoTypes [''TickTock, ''Tock, ''Tick])++instance Arbitrary TickTock where+ arbitrary = fmap TT arbTick where+ arbTick = do+ n <- choose (1,2) :: Gen Int+ case n of+ 1 -> fmap Tick arbTock+ 2 -> return Gong++ arbTock = do+ to <- elements [LoudTock, SoftTock]+ ti <- arbTick+ return $ to ti+++{-+-- a simple binary tree:+data Tree a = Branch (Tree a) (Tree a) + | Leaf a+ deriving (Typeable, Eq)+-}++-- we also test on simple lists ++ -- Don't know the appropriate way to run batch job:+main = sequence_+ [ quickCheck prop_simple_creation+ , quickCheck prop_simple_recursive_movement+ , quickCheck prop_mutual_saving+ , quickCheck prop_simple_moveUp_past_top+ , quickCheck prop_moveUpSaving+ ]++prop_simple_creation :: [Char] -> Bool+prop_simple_creation a = + let z = zipper a+ f = viewf z + a' = close z + in a == f && a == a'++prop_simple_recursive_movement i =+ let i' = abs i `mod` 50 :: Int+ l = replicate i' () + -- test simple descending+ descend 0 z | null $ viewf z = maybe False atTop $ ascend i' z+ | otherwise = False+ descend n z = descend (n-1) (moveTo lTail z)++ -- test ascending by two and one:+ ascend 0 z = return z+ ascend 1 z = moveUp 1 z+ ascend n z = moveUp 2 z >>= ascend (n-2)+ in descend i' $ zipper l+++prop_mutual_saving :: TickTock -> Bool+prop_mutual_saving tt = checkSaving $ descend $ moveTo tickTocks $ zipper tt+ where descend z@(viewf -> Gong) = z+ descend z = descendTock $ moveTo tock z+ descendTock = descend . moveTo tick+ checkSaving z = + let (p,a) = closeSaving z+ z' = restore p a+ lns = savedLens p+ -- closed zipper is equal to original, + in a == tt && + -- restoring brings us back to the end+ viewf z' == Gong && + -- lens rebuilt from SavedPath is equivalent+ getL lns tt == Gong &&+ -- moving to rebuilt lens and moving up gets us back to top:+ (maybe False ((==tt) . viewf) $ + moveUp 1 $ moveTo lns $ zipper tt)++-- check moveUpSaving & Nothing returned from failed cast:+prop_moveUpSaving :: ((),((),(Int,Int))) -> Bool+prop_moveUpSaving = + check . moveTo lSnd . moveTo lSnd . moveTo lSnd . zipper + where check z = maybe False id $ do+ (z', p') <- moveUpSaving 2 z+ let n = viewf z + -- otherwise type is ambiguous:+ typeofz' = z' :: Zipper ((),((),(Int,Int))) ((),(Int,Int))+ n' = viewf $ moveTo p' z'+ -- we successfully moved up and back down again?:+ return $ n == n'++++-- test moveUp past top of Zipper, +prop_simple_moveUp_past_top :: [Int] -> Bool+prop_simple_moveUp_past_top = check . moveUp 2 . moveTo lTail . zipper where+ -- this sig required else type ambiguous:+ check :: Maybe (Zipper1 [Int]) -> Bool+ check = maybe True (const False)
+ pez.cabal view
@@ -0,0 +1,93 @@+Name: pez+Version: 0.0.1+Synopsis: A Potentially-Excellent Zipper library+Homepage: http://coder.bsimmons.name/blog/2011/04/pez-zipper-library-released/+Description:+ PEZ is a generic zipper library. It uses lenses from the 'fclabels' package to+ reference a "location" to move to in the zipper. The zipper is restricted to+ types in the Typeable class, allowing the user to "move up" through complex data+ structures such as mutually-recursive types.+ .+ Both the Typeable class and fclabels lenses can be derived in GHC, making it+ easy for the programmer to use a zipper with a minimum of boilerplate.+ .+ First import the library, which brings in the Typeable and fclabels modules.+ You will also want to enable a few extensions:+ .+ > {-# LANGUAGE TemplateHaskell, DeriveDataTypeable, TypeOperators #-}+ > module Main where+ >+ > import Data.Typeable.Zipper+ .+ Create a datatype, deriving an instance of the Typeable class, and generate a+ lens using functions from fclabels:+ .+ > data Tree a = Node { _leftNode :: Tree a, + > _val :: a, + > _rightNode :: Tree a }+ > | Nil + > deriving (Typeable,Show)+ >+ > $(mkLabelsNoTypes [''Tree])+ .+ Now we can go crazy using Tree in a Zipper:+ .+ > treeBCD = Node (Node Nil 'b' Nil) 'c' (Node Nil 'd' Nil)+ > + > descendLeft :: Zipper1 (Tree a) -> Zipper1 (Tree a)+ > descendLeft z = case (viewf z) of+ > Nil -> z+ > _ -> descendLeft $ moveTo leftNode z+ >+ > insertLeftmost :: a -> Tree a -> Tree a+ > insertLeftmost x = close . setL focus x . descendLeft . zipper+ >+ > treeABCD = insertLeftmost 'a' treeBCD+ .+ Because of the flexibility of fclabels, this zipper library can be used to+ express moving about in reversible computations simply by defining such a lens,+ for instance:+ .+ > stringRep :: (Show b, Read b) => b :-> String+ > stringRep = lens show (const . read)+ .+ Please send any feature requests or bug reports along.++License: BSD3+License-file: LICENSE+Author: Brandon Simmons+Maintainer: brandon.m.simmons@gmail.com++-- A copyright notice.+Copyright: Brandon Simmons, 2011++-- Stability of the pakcage (experimental, provisional, stable...)+Stability: Experimental+Category: Data+Build-type: Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+Extra-source-files: EXAMPLES/Examples.hs, Tests.hs++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.2.3+++Library+ -- Modules exported by the library.+ Exposed-modules: Data.Typeable.Zipper+ + -- Packages needed in order to build this package.+ Build-depends: base >= 4 && < 5+ , fclabels >= 0.11.1.1 && < 0.12+ , thrist >= 0.2 && < 0.3++ Extensions: GeneralizedNewtypeDeriving+ , TypeOperators+ , TemplateHaskell+ , GADTs+ , DeriveDataTypeable+ + -- Modules not exported by this package.+ Other-modules: Data.Record.Label.Prelude