BiGUL 0.9.0.0 → 1.0.0
raw patch · 13 files changed
+1639/−828 lines, 13 filesdep +th-extrasdep −pretty
Dependencies added: th-extras
Dependencies removed: pretty
Files
- BiGUL.cabal +24/−4
- CHANGELOG.md +76/−0
- src/GHC/InOut.hs +37/−11
- src/Generics/BiGUL.hs +195/−0
- src/Generics/BiGUL/AST.hs +0/−227
- src/Generics/BiGUL/Error.hs +111/−181
- src/Generics/BiGUL/Interpreter.hs +136/−75
- src/Generics/BiGUL/Interpreter/Unsafe.hs +42/−38
- src/Generics/BiGUL/Lib.hs +26/−0
- src/Generics/BiGUL/Lib/HuStudies.hs +468/−0
- src/Generics/BiGUL/Lib/List.hs +51/−0
- src/Generics/BiGUL/PatternMatching.hs +104/−0
- src/Generics/BiGUL/TH.hs +369/−292
BiGUL.cabal view
@@ -1,5 +1,5 @@ name: BiGUL-version: 0.9.0.0+version: 1.0.0 synopsis: The Bidirectional Generic Update Language description: Putback-based bidirectional programming allows the programmer to@@ -26,15 +26,35 @@ category: Language, Generics, Lenses build-type: Simple cabal-version: >=1.22+extra-source-files: CHANGELOG.md library- exposed-modules: Generics.BiGUL.AST+ exposed-modules: Generics.BiGUL Generics.BiGUL.Error+ Generics.BiGUL.PatternMatching Generics.BiGUL.Interpreter Generics.BiGUL.Interpreter.Unsafe Generics.BiGUL.TH+ Generics.BiGUL.Lib+ Generics.BiGUL.Lib.HuStudies+ Generics.BiGUL.Lib.List other-modules: GHC.InOut- default-extensions: FlexibleInstances, KindSignatures, GADTs, TupleSections, TemplateHaskell, DeriveDataTypeable- build-depends: base >=4.8 && < 4.9, mtl >=2.2, pretty >=1.1, containers >=0.5, template-haskell >=2.10+ default-extensions: TupleSections,+ ViewPatterns,+ GADTs,+ TypeFamilies,+ TypeOperators,+ EmptyCase,+ ExistentialQuantification,+ TemplateHaskell,+ DeriveDataTypeable,+ FlexibleInstances,+ FlexibleContexts,+ UndecidableInstances+ build-depends: base == 4.8.*,+ mtl >= 2.2,+ containers >= 0.5,+ template-haskell >= 2.10,+ th-extras >= 0.0.0.4 hs-source-dirs: src default-language: Haskell2010
+ CHANGELOG.md view
@@ -0,0 +1,76 @@+1.0.0 Changes+=============++Version 1.0.0 is the first official release, and is *not* compatible with+0.9.0 and earlier development versions.++The targeted GHC version is 7.10; 8.0 support is postponed due to its+non–backward compatible revisions to Template Haskell.++* Module restructuring++ The module structure is refined and simplified, with `Generics.BiGUL.AST`+ changed to `Generics.BiGUL`, pattern matching functions extracted to+ `Generics.BiGUL.PatternMatching`, and `Generics.BiGUL.Lib` created to+ serve as a prelude. More specific library modules are placed under+ `Generics.BiGUL.Lib.`:++ - `Generics.BiGUL.Lib.List` is the place for list-processing library+ programs. For now the only inhabitant is the “BiFluX alignment”.++ - `Generics.BiGUL.Lib.HuStudies` contains some small, concrete examples+ illustrating the use of every BiGUL construct.++* `Generics.BiGUL.Skip` and `Generics.BiGUL.Dep` changed++ - The view type of `Skip` is not restricted to `()` anymore, but when+ skipping, the view should be determined by the source as specified by+ the new functional argument to `Skip` — we can perform `Skip f` on a+ source `s` if and only if the view is `f s`. The old `Skip` can be+ defined in terms of the new one as `Skip (const ())`. There is a helper+ function `skip = Skip . const` defined in `Generics.BiGUL.Lib`.++ - `Dep` has been reverted to the original version, used to ignore the+ second component of the view when it depends on the first (but not+ the source).++* Major changes to `Generics.BiGUL.TH`++ - `deriveBiGULGeneric` now supports `newtype`.++ - The `update` syntax now takes the source pattern as its first argument+ and the view pattern as its second argument. (Previously the view+ pattern comes first.)++ - `rearrV` and `update` now check at compile time whether all view+ information is used (forbidding wildcard in view patterns and requiring+ all view variables are used); also `rearrS` and `rearrV` check whether+ their first argument is a one-argument lambda-expression.++ - The branch construction syntax has been slimmed down to just four+ functions: `normal`, `normalSV`, `adaptive`, and `adaptiveSV`.++ - Normal branch constructing functions now take an additional argument+ specifying on the source an “exit condition”, which should be satisfied+ by the updated source after the branch body is executed. All the exit+ conditions in a `Case` statement should (ideally) be disjoint.+ Overlapping exit conditions are still allowed for fast prototyping,+ though — the putback semantics of 'Case' will compute successfully as+ long as the ranges of the branches are disjoint (regardless of whether+ the exit conditions are specified precisely enough).++* Error-reporting mechanism overhauled++ - The types of `put` and `get` from `Generics.BiGUL.Interpreter` are+ changed to produce simply `Maybe` results. When execution fails+ (producing `Nothing`), invoke `putTrace` and `getTrace` to see the+ exact failure and the execution trace leading to the failure.++ - The execution traces include intermediate sources and views; the types+ used in a BiGUL program are thus required to be instances of `Show`.++* Show instances for BiGUL programs removed++ There are two reasons: Functions, which are everywhere in BiGUL programs,+ cannot be shown; and worse, printing of recursive BiGUL programs will not+ terminate.
src/GHC/InOut.hs view
@@ -1,26 +1,57 @@-{-# LANGUAGE TypeFamilies, UndecidableInstances, FlexibleInstances, FlexibleContexts, TypeOperators #-}- ----------------------------------------------------------------------------- -- | -- Module : GHC.InOut -- Copyright : (C) 2013 Hugo Pacheco--- License : BSD-style (see the file LICENSE)+-- License : BSD-style (see below) -- Maintainer : Hugo Pacheco <hpacheco@nii.ac.jp> -- Stability : provisional -- -- Generic sums of products representation for algebraic data types--- -----+-- @+-- Copyright (c) 2013, National Institute of Informatics++-- 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.++-- * The names of contributors may not 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.+-- @ ----------------------------------------------------------------------------+ module GHC.InOut where import GHC.Generics + class (Generic a,ToFromRep (Rep a)) => InOut a where inn :: F a -> a out :: a -> F a- + type family Flatten (f :: * -> *) :: * type F a = Flatten (Rep a) @@ -55,8 +86,3 @@ instance (Generic a,ToFromRep (Rep a)) => InOut a where inn = to . toRep out = fromRep . from----instance InOut [a] where--- inn s = either (\() -> []) (\(x,xs) -> x:xs) s--- out l = case l of { [] -> Left (); (x:xs) -> Right (x,xs) }-
+ src/Generics/BiGUL.hs view
@@ -0,0 +1,195 @@+-- | This is the main module defining the syntax of BiGUL.+-- "Generics.BiGUL.TH" provides some higher-level syntax for writing BiGUL programs.+-- See "Generics.BiGUL.Lib.HuStudies" for some small, illustrative examples.+-- To execute BiGUL programs, use 'Generics.BiGUL.Interpreter.put' and 'Generics.BiGUL.Interpreter.get'+-- from "Generics.BiGUL.Interpreter".++module Generics.BiGUL(+ -- * Main syntax+ BiGUL(..)+ , CaseBranch(..)+ -- * Rearrangement syntax+ -- | The following pattern and expression syntax for rearrangement operations are designed to be type-safe+ -- but not intended to be programmer-friendly. The programmer is expected to use the higher-level syntax+ -- from "Generics.BiGUL.TH", which desugars into the following raw syntax.+ -- For more detail about patterns and expressions, see "Generics.BiGUL.PatternMatching".+ , Pat(..)+ , Var(..)+ , Direction(..)+ , Expr(..)) where++import GHC.InOut+++-- | This is the datatype of BiGUL programs, as a GADT indexed with the source and view types.+-- Most of the types appearing in a BiGUL program should be instances of 'Show' to enable error reporting.+-- Before GHC 8, haddock does not support documentation for GADT constructors;+-- for GHC 7.10.*, see the source for the description of each constructor and its arguments.+data BiGUL s v where++ -- Abort computation and emit an error message.+ Fail :: String -- error message+ -> BiGUL s v++ -- Keep the source unchanged, with the side condition that the view can be completely determined from the source.+ -- Use Generics.BiGUL.Lib.skip when the view is a constant.+ Skip :: Eq v+ => (s -> v) -- how the view can be computed from the source+ -> BiGUL s v++ -- Replace the source with the view (which should have the same type as the source).+ Replace :: BiGUL s s++ -- When the source and view are both pairs, perform update on the first/second source and view components+ -- using the first/second inner program.+ Prod :: (Show s, Show s', Show v, Show v')+ => BiGUL s v -- program for updating the first components+ -> BiGUL s' v' -- program for updating the second components+ -> BiGUL (s, s') (v, v')++ -- Rearrange the source into an intermediate form, which is updated by the inner program,+ -- and then invert the rearrangement.+ -- Instead of using 'RearrS' directly, use 'Generics.BiGUL.TH.rearrS' instead,+ -- which offers a more intuitive syntax.+ -- Note that the inner program should make sure that the updated source still+ -- retains the intermediate form (so the inversion can succeed).+ RearrS :: (Show s', Show v)+ => Pat s env con -- pattern for the original source+ -> Expr env s' -- expression computing the intermediate source+ -> BiGUL s' v -- program for updating the intermediate source+ -> BiGUL s v++ -- Rearrange the view into a new one before continuing with the remaining program.+ -- To guarantee well-behavedness, the expression should use all variables in the pattern.+ -- Instead of using 'RearrV' directly, use 'Generics.BiGUL.TH.rearrV' instead,+ -- which offers a more intuitive syntax and checks whether all pattern variables are used.+ RearrV :: (Show s, Show v')+ => Pat v env con -- pattern for the original view+ -> Expr env v' -- expression computing the new view+ -> BiGUL s v' -- remaining program+ -> BiGUL s v++ -- When the view is a pair and the second component depends entirely on the first one,+ -- discard the second component and continue with the remaining program.+ Dep :: (Eq v', Show s, Show v)+ => (v -> v') -- how the second component of the view can be computed from the first+ -> BiGUL s v -- remaining program+ -> BiGUL s (v, v')++ -- Case analysis on both the source and view.+ Case :: [(s -> v -> Bool, CaseBranch s v)] -- branches, each of which consists of+ -- a main condition (on both the source and view)+ -- and an inner action+ -> BiGUL s v++ -- Standard composition of bidirectional transformations.+ Compose :: (Show s, Show m, Show v)+ => BiGUL s m+ -> BiGUL m v+ -> BiGUL s v++infixr 1 `Prod`+infixr 1 `Compose`++-- | A branch used in 'Case' (whose type is parametrised by the source and view types)+-- can be either 'Normal' or 'Adaptive'.+-- The exit conditions specified in 'Normal' branches should (ideally) be disjoint.+-- Overlapping exit conditions are still allowed for fast prototyping, though —+-- the putback semantics of 'Case' will compute successfully as long as the ranges+-- of the branches are disjoint (regardless of whether the exit conditions are+-- specified precisely enough).+data CaseBranch s v =+ -- | A 'Normal' branch contains an inner program, which should update the source such that+ -- both the main condition (on both the source and view) and the exit condition (on the source) are satisfied.+ Normal (BiGUL s v) (s -> Bool)+ -- | An 'Adaptive' branch contains an adaptation function, which should modify the source such that+ -- a 'Normal' branch is applicable.+ | Adaptive (s -> v -> s)+++-- | The datatype of patterns is indexed by three types: the type of values to which a pattern is applicable,+-- the type of environments resulting from pattern matching, and the type of containers used during+-- inverse evaluation of expressions.+data Pat a env con where++ -- Variable pattern, the value extracted from which can be duplicated.+ PVar :: Eq a+ => Pat a (Var a) (Maybe a)++ -- Variable pattern, the value extracted from which cannot be duplicated.+ PVar' :: Pat a (Var a) (Maybe a)++ -- Constant pattern.+ PConst :: Eq a+ => a -- constant to be matched+ -> Pat a () ()++ -- Product pattern.+ PProd :: Pat a a' a'' -- left-hand side pattern+ -> Pat b b' b'' -- right-hand side pattern+ -> Pat (a, b) (a', b') (a'', b'')++ -- Left pattern, matching values of shape `Left x :: Either a b` for some `x :: a`.+ PLeft :: Pat a a' a'' -- inner pattern+ -> Pat (Either a b) a' a''++ -- Right pattern, matching values of shape `Right y :: Either a b` for some `y :: b`.+ PRight :: Pat b b' b'' -- inner pattern+ -> Pat (Either a b) b' b''++ -- Constructor pattern, unwrapping a value to its sum-of-products representation.+ -- (Invoke 'Generics.BiGUL.TH.deriveBiGULGenerics' on the datatype involved first.)+ PIn :: InOut a+ => Pat (F a) b c -- inner pattern+ -> Pat a b c++infixr 1 `PProd`++-- | A marker for variable positions in environment types.+newtype Var a = Var a deriving Show++-- | Directions point to a variable position (marked by 'Var') in an environment.+-- Their type is indexed by the environment type and the type of the variable position being pointed to.+data Direction env a where++ -- Point to the current variable position.+ DVar :: Direction (Var a) a++ -- Point to the left part of the environment.+ DLeft :: Direction a t+ -> Direction (a, b) t++ -- Point to the right part of the environment.+ DRight :: Direction b t -> Direction (a, b) t++-- | Expressions are patterns whose variable positions contain directions pointing into some environment.+-- Their type is indexed by the environment type and the type of the expressed value.+data Expr env a where++ -- Direction expression, referring to a value in the environment.+ EDir :: Direction env a+ -> Expr env a++ -- Constant expression.+ EConst :: (Eq a)+ => a -- constant+ -> Expr env a++ -- Product expression.+ EProd :: Expr env a -- left-hand side expression+ -> Expr env b -- right-hand side expression+ -> Expr env (a, b)++ -- Left expression (producing an 'Either'-value).+ ELeft :: Expr env a+ -> Expr env (Either a b)++ -- Right expression (producing an 'Either'-value).+ ERight :: Expr env b+ -> Expr env (Either a b)++ -- Constructor expression, wrapping a sum-of-products representation into data.+ -- (Invoke 'Generics.BiGUL.TH.deriveBiGULGenerics' on the datatype involved first.)+ EIn :: (InOut a) => Expr env (F a) -> Expr env a++infixr 1 `EProd`
− src/Generics/BiGUL/AST.hs
@@ -1,227 +0,0 @@-module Generics.BiGUL.AST- (BiGUL(..), CaseBranch(..), Pat(..), Direction(..), Expr(..),- deconstruct, construct, eval, uneval, emptyContainer, fromContainerS, fromContainerV) where--import Generics.BiGUL.Error-import Control.Monad.Except-import GHC.InOut-import Data.List(intersperse)---data CaseBranch s v = Normal (BiGUL s v) (s -> Bool) | Adaptive (s -> v -> s)--instance Show (CaseBranch s v) where- show (Normal bigul _) = "Normal " ++ show bigul- show (Adaptive _ ) = "Adaptive <adaptive function>"--data BiGUL :: * -> * -> * where- Fail :: String -> BiGUL s v- Skip :: BiGUL s ()- Replace :: BiGUL s s- Prod :: BiGUL s v -> BiGUL s' v' -> BiGUL (s, s') (v, v')- RearrS :: Pat s env con -> Expr env s' -> BiGUL s' v -> BiGUL s v- RearrV :: Pat v env con -> Expr env v' -> BiGUL s v' -> BiGUL s v- Dep :: (Eq v') => BiGUL s v -> (s -> v -> v') -> BiGUL s (v, v')- Case :: [(s -> v -> Bool, CaseBranch s v)] -> BiGUL s v- Compose :: BiGUL s u -> BiGUL u v -> BiGUL s v--infixr 1 `Prod`--instance Show (BiGUL s v) where- show (Fail s) = "Fail: " ++ s- show Skip = "Skip"- show Replace = "Replace"- show (Dep b _) = "(Dep <dependency function> " ++ show b ++ ")"- show (Case bs) = "(Case [" ++ unwords (intersperse "\n" (map (\(_,b) -> "(predicate, " ++ show b ++ ")") bs)) ++ " ])"- show _ = "Unknown BiGUL program in show"--newtype Var a = Var a--instance Show a => Show (Var a) where- show (Var a) = "Var: " ++ show a---- Pat (view type) (environment type) (container type)-data Pat :: * -> * -> * -> * where- PVar :: Eq a => Pat a (Var a) (Maybe a)- PVar' :: Pat a (Var a) (Maybe a)- PConst :: (Eq a) => a -> Pat a () ()- PProd :: Pat a a' a'' -> Pat b b' b'' -> Pat (a, b) (a', b') (a'', b'')- PLeft :: Pat a a' a'' -> Pat (Either a b) a' a''- PRight :: Pat b b' b'' -> Pat (Either a b) b' b''- PIn :: InOut a => Pat (F a) b c -> Pat a b c--instance Show (Pat v e c) where- show PVar = "PVar"- show PVar' = "PVar'"- show (PConst c) = "PConst"- show (PProd rp1 rp2) = "(PProd " ++ show rp1 ++ " " ++ show rp2 ++ ")"- show (PLeft rp) = "(PLeft " ++ show rp ++ ")"- show (PRight rp) = "(PRight " ++ show rp ++ ")"- show (PIn rp) = "(PIn " ++ show rp ++ ")"- -- show _ = "show error in Pat"--deconstruct :: Pat a env con -> a -> Either (PatExprDirError a) env-deconstruct PVar a = return (Var a)-deconstruct PVar' a = return (Var a)-deconstruct (PConst c) a = if c == a then return () else throwError PEDConstantMismatch-deconstruct (PProd patl patr) (al, ar) = liftM2 (,) (liftE PEDProdLeft (deconstruct patl al))- (liftE PEDProdRight (deconstruct patr ar))-deconstruct (PLeft patl) (Left al) = liftE PEDEitherLeft (deconstruct patl al)-deconstruct pat@(PLeft _) a = throwError PEDEitherMismatch-deconstruct (PRight patr) (Right ar) = liftE PEDEitherRight (deconstruct patr ar)-deconstruct pat@(PRight _) a = throwError PEDEitherMismatch-deconstruct (PIn pat) a = liftE PEDIn (deconstruct pat (out a))--construct :: Pat a env con -> env -> a-construct PVar (Var a) = a-construct PVar' (Var a) = a-construct (PConst c) _ = c-construct (PProd patl patr) (al, ar) = (construct patl al, construct patr ar)-construct (PLeft patl) al = Left (construct patl al)-construct (PRight patr) ar = Right (construct patr ar)-construct (PIn pat) a = inn (construct pat a)--fromContainerV :: Pat v env con -> con -> Either (PatExprDirError v) env-fromContainerV PVar Nothing = throwError PEDValueUnrecoverable-fromContainerV PVar (Just v) = return (Var v)-fromContainerV PVar' Nothing = throwError PEDValueUnrecoverable-fromContainerV PVar' (Just v) = return (Var v)-fromContainerV (PConst c) con = return ()-fromContainerV (PProd patl patr) (conl, conr) = liftM2 (,) (liftE PEDProdLeft (fromContainerV patl conl))- (liftE PEDProdRight (fromContainerV patr conr))-fromContainerV (PLeft pat) con = liftE PEDEitherLeft (fromContainerV pat con)-fromContainerV (PRight pat) con = liftE PEDEitherRight (fromContainerV pat con)-fromContainerV (PIn pat) con = liftE PEDIn (fromContainerV pat con)--fromContainerS :: Pat s env con -> env -> con -> env-fromContainerS PVar (Var s) Nothing = (Var s)-fromContainerS PVar (Var s) (Just s') = (Var s')-fromContainerS PVar' (Var s) Nothing = (Var s)-fromContainerS PVar' (Var s) (Just s') = (Var s')-fromContainerS (PConst c) _ _ = ()-fromContainerS (PProd lpat rpat) (env, env') (con, con') = (fromContainerS lpat env con, fromContainerS rpat env' con')-fromContainerS (PLeft pat) env con = fromContainerS pat env con-fromContainerS (PRight pat) env con = fromContainerS pat env con-fromContainerS (PIn pat) env con = fromContainerS pat env con--emptyContainer :: Pat v env con -> con-emptyContainer PVar = Nothing-emptyContainer PVar' = Nothing-emptyContainer (PConst c) = ()-emptyContainer (PProd rpatl rpatr) = (emptyContainer rpatl, emptyContainer rpatr)-emptyContainer (PLeft pat ) = emptyContainer pat-emptyContainer (PRight pat ) = emptyContainer pat-emptyContainer (PIn pat ) = emptyContainer pat---- You need to explicitly specify the type arguments at the type level when using the Direction type.--- From type, you could know the type of the data you want.--- !comment: DMaybe did not used.-data Direction :: * -> * -> * where- DVar :: Direction (Var a) a- DLeft :: Direction a t -> Direction (a, b) t- DRight :: Direction b t -> Direction (a, b) t--instance Show (Direction a t) where- show DVar = "DVar"- show (DLeft dir) = "(DLeft " ++ show dir ++ ")"- show (DRight dir) = "(DRight " ++ show dir ++ ")"--retrieve :: Direction a t -> a -> t-retrieve DVar (Var x) = x-retrieve (DLeft p) (x, y) = retrieve p x-retrieve (DRight p) (x, y) = retrieve p y--data Expr :: * -> * -> * where- EDir :: Direction orig a -> Expr orig a- EConst :: Eq a => a -> Expr orig a- EIn :: InOut a => Expr orig (F a) -> Expr orig a- EProd :: Expr orig a -> Expr orig b -> Expr orig (a, b)- ELeft :: Expr orig a -> Expr orig (Either a b)- ERight :: Expr orig b -> Expr orig (Either a b)--instance Show (Expr orig a) where- show (EDir dir) = "(EDir " ++ show dir ++ ")"- show (EConst c) = "EConst"- show (EProd e1 e2) = "(EProd " ++ show e1 ++ " " ++ show e2 ++ ")"- show (ELeft e) = "(ELeft " ++ show e ++ ")"- show (ERight e) = "(ERight " ++ show e ++ ")"- show (EIn e) = "(EIn " ++ show e ++ ")"--eval :: Expr env a' -> env -> a'-eval (EDir dir) env = retrieve dir env-eval (EConst c) env = c-eval (EIn expr) env = inn (eval expr env)-eval (EProd exprl exprr) env = (eval exprl env, eval exprr env)-eval (ELeft expr ) env = Left $ eval expr env-eval (ERight expr ) env = Right $ eval expr env---- The goal is to update the "Maybe" con to fill in proper values.--- con follow the structure of Pat--- we have updated value a', which follows the structure of Expr-uneval :: Pat a env con -> Expr env a' -> a' -> con -> Either (PatExprDirError a') con-uneval pat (EDir dir) a' con = unevalDir pat dir a' con-uneval pat (EConst c) a' con = if c == a' then return con else throwError PEDConstantMismatch-uneval pat (EIn expr) a' con = liftE PEDIn (uneval pat expr (out a') con)-uneval pat (EProd exprl exprr) (al', ar') con = liftE PEDProdLeft (uneval pat exprl al' con) >>= liftE PEDProdRight . uneval pat exprr ar'-uneval pat (ELeft expr) (Left al') con = liftE PEDEitherLeft (uneval pat expr al' con)-uneval pat expr@(ELeft _) a' con = throwError PEDEitherMismatch-uneval pat (ERight expr) (Right ar') con = liftE PEDEitherRight (uneval pat expr ar' con)-uneval pat expr@(ERight _) a' con = throwError PEDEitherMismatch--unevalDir :: Pat a env con -> Direction env a' -> a' -> con -> Either (PatExprDirError a') con-unevalDir PVar DVar a' (Just a'') = if a' == a''- then return (Just a')- else throwError (PEDIncompatibleUpdates a' a'')-unevalDir PVar DVar a' Nothing = return (Just a')-unevalDir PVar' DVar a' (Just a'') = throwError (PEDMultipleUpdates a' a'')-unevalDir PVar' DVar a' Nothing = return (Just a')-unevalDir (PConst c) _ a' con = return con-unevalDir (PProd patl patr) (DLeft dir) a' (conl, conr) = liftM (, conr) (unevalDir patl dir a' conl)-unevalDir (PProd patl patr) (DRight dir) a' (conl, conr) = liftM (conl ,) (unevalDir patr dir a' conr)-unevalDir (PLeft patl ) dir a' con = unevalDir patl dir a' con-unevalDir (PRight patr ) dir a' con = unevalDir patr dir a' con-unevalDir (PIn pat ) dir a' con = unevalDir pat dir a' con---- TODO: static check of full embedding--- checkFullEmbed :: BiGUL m s v -> Bool--- checkFullEmbed Fail = True--- checkFullEmbed Skip = True--- checkFullEmbed Replace = True--- checkFullEmbed (RearrS pat expr bigul) = checkFullEmbed bigul--- checkFullEmbed (RearrV pat expr bigul) = checkRearr expr pat && checkFullEmbed bigul--- checkFullEmbed (Dep bigul f) = checkFullEmbed bigul--- checkFullEmbed (Case branches) = and (map checkBranch branches)---- checkBranch :: (s -> v -> Bool, CaseBranch m s v) -> Bool--- checkBranch (cond, Normal bigul _) = checkFullEmbed bigul--- checkBranch (cond, _) = True---- checkRearr :: Expr env v' -> Pat v env con -> Bool--- checkRearr expr pat = checkCon pat (abstractUpdateCon expr pat (emptyContainer pat))---- abstractUpdateCon :: Expr env a' -> Pat a env con -> con -> con--- abstractUpdateCon (EDir dir) pat con = abstractUpdateDir pat dir con--- abstractUpdateCon (EConst c) pat con = con--- abstractUpdateCon (EIn expr) pat con = abstractUpdateCon expr pat con--- abstractUpdateCon (EProd exprl exprr) pat con = abstractUpdateCon exprr pat (abstractUpdateCon exprl pat con)--- abstractUpdateCon (ELeft expr) pat con = abstractUpdateCon expr pat con--- abstractUpdateCon (ERight expr) pat con = abstractUpdateCon expr pat con---- abstractUpdateDir :: Pat a env con -> Direction env a' -> con -> con--- abstractUpdateDir PVar DVar Nothing = Just undefined--- abstractUpdateDir PVar DVar (Just _) = Just undefined--- abstractUpdateDir (PConst c) _ con = con--- abstractUpdateDir (PProd patl patr) (DLeft dir) (conl, conr) = (abstractUpdateDir patl dir conl, conr)--- abstractUpdateDir (PProd patl patr) (DRight dir) (conl, conr) = (conl, abstractUpdateDir patr dir conr)--- abstractUpdateDir (PLeft patl ) dir con = abstractUpdateDir patl dir con--- abstractUpdateDir (PRight patr ) dir con = abstractUpdateDir patr dir con--- abstractUpdateDir (PIn pat ) dir con = abstractUpdateDir pat dir con---- checkCon :: Pat v env con -> con -> Bool--- checkCon PVar (Just _) = True--- checkCon PVar Nothing = False--- checkCon (PConst c) _ = True--- checkCon (PProd patl patr) (conl, conr) = checkCon patl conl && checkCon patr conr--- checkCon (PLeft patl) con = checkCon patl con--- checkCon (PRight patr) con = checkCon patr con--- checkCon (PIn pat ) con = checkCon pat con
src/Generics/BiGUL/Error.hs view
@@ -1,191 +1,121 @@+-- | Datatypes for error traces, produced by the standard interpreters in "Generics.BiGUL.Interpreter".+ module Generics.BiGUL.Error where -import GHC.InOut-import Text.PrettyPrint+import Generics.BiGUL +import Control.Monad+import Data.List -class PrettyPrintable a where- toDoc :: a -> Doc -data PutError :: * -> * -> * where- PFail :: String -> PutError s v- PSourcePatternMismatch :: PatExprDirError s -> PutError s v- PViewPatternMismatch :: PatExprDirError v -> PutError s v- PUnevalFailed :: PatExprDirError s' -> PutError s v- PDependencyMismatch :: s -> PutError s (v, v')- PNoIntermediateSource :: GetError s v' -> PutError s v- PCaseExhausted :: PutError s v- PAdaptiveBranchRevisited :: PutError s v- PAdaptiveBranchMatched :: PutError s v- PPreviousBranchMatched :: PutError s v- PBranchPredictionIncorrect :: PutError s v- PPostVerificationFailed :: PutError s v- PBranchUnmatched :: PutError s v- --- PProdLeft :: s -> v -> PutError s v -> PutError (s, s') (v, v')- PProdRight :: s' -> v' -> PutError s' v' -> PutError (s, s') (v, v')- PRearrS :: s' -> v -> PutError s' v -> PutError s v- PRearrV :: s -> v' -> PutError s v' -> PutError s v- PDep :: s -> v -> PutError s v -> PutError s (v, v')- PComposeLeft :: a -> b -> PutError a b -> PutError a c- PComposeRight :: b -> c -> PutError b c -> PutError a c- PBranch :: Int -> PutError s v -> PutError s v--incrBranchNo :: PutError s v -> PutError s v-incrBranchNo (PBranch i e) = PBranch (i+1) e-incrBranchNo e = e--instance Show (PutError s v) where- show (PFail str) = "fail: " ++ str- show (PSourcePatternMismatch e) = show e- show (PViewPatternMismatch e) = show e- show (PUnevalFailed e) = show e- show (PDependencyMismatch _) = "dependency mismatch"- show (PNoIntermediateSource e) = show e- show PCaseExhausted = "case exhausted"- show PAdaptiveBranchRevisited = "adaptive branch revisited"- show PAdaptiveBranchMatched = "adaptive branch matched"- show PPreviousBranchMatched = "previous branch matched"- show PBranchPredictionIncorrect = "branch prediction incorrect"- show PPostVerificationFailed = "post-verification failed"- show PBranchUnmatched = "branch unmatched"- show (PProdLeft _ _ e) = show e- show (PProdRight _ _ e) = show e- show (PRearrS _ _ e) = show e- show (PRearrV _ _ e) = show e- show (PDep _ _ e) = show e- show (PComposeLeft _ _ e) = show e- show (PComposeRight _ _ e) = show e- show (PBranch _ e) = show e--indent :: Doc -> Doc-indent = nest 2--instance PrettyPrintable (PutError s v) where- toDoc e@(PFail str) = text (show e)- toDoc (PSourcePatternMismatch e) = text "source pattern mismatch" $+$ indent (toDoc e)- toDoc (PViewPatternMismatch e) = text "view pattern mismatch" $+$ indent (toDoc e)- toDoc (PUnevalFailed e) = text "inverse evaluation failed" $+$ indent (toDoc e)- toDoc e@(PDependencyMismatch _) = text (show e)- toDoc (PNoIntermediateSource e) = text "computation of intermediate source failed" $+$ indent (toDoc e)- toDoc e@PCaseExhausted = text (show e)- toDoc e@PAdaptiveBranchRevisited = text (show e)- toDoc e@PAdaptiveBranchMatched = text (show e)- toDoc e@PPreviousBranchMatched = text (show e)- toDoc e@PBranchPredictionIncorrect = text (show e)- toDoc e@PPostVerificationFailed = text (show e)- toDoc e@PBranchUnmatched = text (show e)- toDoc (PProdLeft _ _ e) = text "on the left-hand side of Prod" $+$ toDoc e- toDoc (PProdRight _ _ e) = text "on the right-hand side of Prod" $+$ toDoc e- toDoc (PRearrS _ _ e) = text "in RearrS" $+$ toDoc e- toDoc (PRearrV _ _ e) = text "in RearrV" $+$ toDoc e- toDoc (PDep _ _ e) = text "in Dep" $+$ toDoc e- toDoc (PComposeLeft _ _ e) = text "on the left-hand side of Comp" $+$ toDoc e- toDoc (PComposeRight _ _ e) = text "on the right-hand side of Comp" $+$ toDoc e- toDoc (PBranch i e) = text ("in Case branch " ++ show i) $+$ toDoc e--data GetError :: * -> * -> * where- GFail :: String -> GetError s v- GSourcePatternMismatch :: PatExprDirError s -> GetError s v- GUnevalFailed :: PatExprDirError s' -> GetError s v- GViewRecoveringIncomplete :: PatExprDirError v' -> GetError s v- GCaseExhausted :: [GetError s v] -> GetError s v- GPreviousBranchMatched :: GetError s v- GPostVerificationFailed :: GetError s v- GBranchUnmatched :: GetError s v- GAdaptiveBranchMatched :: GetError s v- --- GProdLeft :: s -> GetError s v -> GetError (s, s') (v, v')- GProdRight :: s' -> GetError s' v' -> GetError (s, s') (v, v')- GRearrS :: s' -> GetError s' v -> GetError s v- GRearrV :: s -> GetError s v' -> GetError s v- GDep :: s -> GetError s v -> GetError s (v, v')- GComposeLeft :: a -> GetError a b -> GetError a c- GComposeRight :: b -> GetError b c -> GetError a c- GBranch :: Int -> GetError s v -> GetError s v--addCurrentBranchError :: GetError s v -> GetError s v -> GetError s v-addCurrentBranchError e0 (GCaseExhausted es) = GCaseExhausted (e0:es)-addCurrentBranchError e0 (GBranch i e) = GBranch (i+1) e--instance Show (GetError s v) where- show (GFail str) = "fail: " ++ str- show (GSourcePatternMismatch e) = show e- show (GUnevalFailed e) = show e- show (GViewRecoveringIncomplete e) = show e- show (GCaseExhausted _) = "case exhausted"- show GPreviousBranchMatched = "previous branch matched"- show GPostVerificationFailed = "post-verification failed"- show GBranchUnmatched = "branch unmatched"- show GAdaptiveBranchMatched = "adaptive branch matched"- show (GProdLeft _ e) = show e- show (GProdRight _ e) = show e- show (GRearrS _ e) = show e- show (GRearrV _ e) = show e- show (GDep _ e) = show e- show (GComposeLeft _ e) = show e- show (GComposeRight _ e) = show e- show (GBranch _ e) = show e+-- | Execution traces, which log the operations executed, intermediate sources and views, and reasons of eventual failure.+data BiGULTrace = BTSuccess+ -- ^ Execution successfully produces a result.+ | BTError BiGULError+ -- ^ Execution fails to produce a result.+ | forall s v. (Show s, Show v) => BTNextSV String s v BiGULTrace+ -- ^ An intermediate step with the current source and view.+ | forall s. (Show s) => BTNextS String s BiGULTrace+ -- ^ An intermediate step with the current source.+ | BTBranch Int BiGULTrace+ -- ^ Inside a branch. /Notes that branch numbering starts from 0./+ | BTBranches [BiGULTrace]+ -- ^ All branches fail. -instance PrettyPrintable (GetError s v) where- toDoc e@(GFail str) = text (show e)- toDoc (GSourcePatternMismatch e) = text "source pattern mismatch" $+$ indent (toDoc e)- toDoc (GUnevalFailed e) = text "inverse evaluation failed" $+$ indent (toDoc e)- toDoc (GViewRecoveringIncomplete e) = text "view recovering incomplete" $+$ indent (toDoc e)- toDoc e@(GCaseExhausted es) = text (show e) $+$- foldr ($+$) empty- (zipWith (\i doc -> text ("branch " ++ show i) $+$ indent doc)- [0..]- (map toDoc es))- toDoc e@GPreviousBranchMatched = text (show e)- toDoc e@GPostVerificationFailed = text (show e)- toDoc e@GBranchUnmatched = text (show e)- toDoc e@GAdaptiveBranchMatched = text (show e)- toDoc (GProdLeft _ e) = text "on the left-hand side of Prod" $+$ toDoc e- toDoc (GProdRight _ e) = text "on the right-hand side of Prod" $+$ toDoc e- toDoc (GRearrS _ e) = text "in RearrS" $+$ toDoc e- toDoc (GRearrV _ e) = text "in RearrV" $+$ toDoc e- toDoc (GDep _ e) = text "in Dep" $+$ toDoc e- toDoc (GComposeLeft _ e) = text "on the left-hand side of Comp" $+$ toDoc e- toDoc (GComposeRight _ e) = text "on the right-hand side of Comp" $+$ toDoc e- toDoc (GBranch i e) = text ("in Case branch " ++ show i) $+$ toDoc e+instance Show BiGULTrace where+ show BTSuccess = "success"+ show (BTError e) = show e+ show (BTNextSV str s v t) = str ++ "\n" +++ " source = " ++ show s ++ "\n" +++ " view = " ++ show v ++ "\n" ++ show t+ show (BTNextS str s t) = str ++ "\n" +++ " source = " ++ show s ++ "\n" ++ show t+ show (BTBranch i t) = "inside branch " ++ show i ++ "\n" ++ show t+ show (BTBranches ts) = "all cases fail\n" +++ concat (intersperse "\n" (concat+ (zipWith (\i t -> (" branch " ++ show i ++ ":") :+ map (" " ++) (lines (show t))) [0..] ts))) -data PatExprDirError :: * -> * where- PEDConstantMismatch :: PatExprDirError a- PEDEitherMismatch :: PatExprDirError (Either a b)- PEDValueUnrecoverable :: PatExprDirError a- PEDIncompatibleUpdates :: a -> a -> PatExprDirError a- PEDMultipleUpdates :: a -> a -> PatExprDirError a- --- PEDProdLeft :: PatExprDirError a -> PatExprDirError (a, b)- PEDProdRight :: PatExprDirError b -> PatExprDirError (a, b)- PEDEitherLeft :: PatExprDirError a -> PatExprDirError (Either a b)- PEDEitherRight :: PatExprDirError b -> PatExprDirError (Either a b)- PEDIn :: InOut a => PatExprDirError (F a) -> PatExprDirError a+-- | Datatype of atomic errors.+data BiGULError = BEFail String+ -- ^ 'Generics.BiGUL.Fail' is executed.+ | BESkipMismatch+ -- ^ When executing @Skip f@ on a source @s@, the view is not @f s@.+ | BESourcePatternMismatch PatError+ -- ^ The source does not match with a pattern.+ | BEViewPatternMismatch PatError+ -- ^ The view does not match with a pattern.+ | BEInvRearrFailed PatError+ -- ^ The inverse rearrangement fails.+ | BEViewRecoveringFailed PatError+ -- ^ The view cannot be reconstructed.+ | BEDependencyMismatch+ -- ^ When executing @Dep f _@ on a view pair @(v, v')@, the second component @v'@ is not @f v@.+ | BECaseExhausted+ -- ^ No branch in a 'Generics.BiGUL.Case' statement is applicable.+ | BEAdaptiveBranchRevisited+ -- ^ After adaptation, execution still goes into an adaptative branch.+ | BEPreviousBranchMatched+ -- ^ After execution of a branch, the computed source and view+ -- satisfy the main condition of a previous branch.+ | BEExitConditionFailed+ -- ^ After execution of a branch, the updated source fails to satisfy the exit condition.+ | BEPostVerificationFailed+ -- ^ After execution of a branch, the computed source and view do not satisfy the main condition.+ | BEBranchUnmatched+ -- ^ The main condition is not satisfied by the source and view (and hence the branch is not chosen).+ | BEAdaptiveBranchMatched+ -- ^ The branch is adaptive and hence ignored. -instance Show (PatExprDirError a) where- show PEDConstantMismatch = "constant mismatch"- show PEDEitherMismatch = "either value mismatch"- show PEDValueUnrecoverable = "value unrecoverable"- show (PEDIncompatibleUpdates _ _) = "incompatible updates"- show (PEDMultipleUpdates _ _) = "multiple updates"- show (PEDProdLeft e) = show e- show (PEDProdRight e) = show e- show (PEDEitherLeft e) = show e- show (PEDEitherRight e) = show e- show (PEDIn e) = show e+instance Show BiGULError where+ show (BEFail str) = "fail statement executed\n " ++ str+ show BESkipMismatch = "view not determined by the source"+ show (BESourcePatternMismatch e) = "source pattern mismatch\n " ++ show e+ show (BEViewPatternMismatch e) = "view pattern mismatch\n " ++ show e+ show (BEInvRearrFailed e) = "inverse rearrangement failed\n " ++ show e+ show (BEViewRecoveringFailed e) = "view recovering failed\n " ++ show e+ show BEDependencyMismatch = "second view component not determined by the first"+ show BECaseExhausted = "case exhausted"+ show BEAdaptiveBranchRevisited = "adaptive branch revisited"+ show BEPreviousBranchMatched = "previous branch matched after branch execution"+ show BEExitConditionFailed = "exit condition not satisfied after branch execution"+ show BEPostVerificationFailed = "main condition not satisfied after branch execution"+ show BEBranchUnmatched = "main condition not satisfied"+ show BEAdaptiveBranchMatched = "adaptive branch ignored" -instance PrettyPrintable (PatExprDirError a) where- toDoc e@PEDConstantMismatch = text (show e)- toDoc e@PEDEitherMismatch = text (show e)- toDoc e@PEDValueUnrecoverable = text (show e)- toDoc e@(PEDIncompatibleUpdates _ _) = text (show e)- toDoc e@(PEDMultipleUpdates _ _) = text (show e)- toDoc (PEDProdLeft e) = text "on the left-hand side of PProd" $+$ toDoc e- toDoc (PEDProdRight e) = text "on the right-hand side of PProd" $+$ toDoc e- toDoc (PEDEitherLeft e) = text "inside PLeft" $+$ toDoc e- toDoc (PEDEitherRight e) = text "inside PRight" $+$ toDoc e- toDoc (PEDIn e) = text "inside PIn" $+$ toDoc e+-- | Pattern matching errors, which also explain where the matching fails.+data PatError = PEConstantMismatch+ -- ^ A constant pattern/expression is matched with a different value.+ | PELeftMismatch+ -- ^ A 'Left' pattern/expression is matched with a 'Right' value.+ | PERightMismatch+ -- ^ A 'Right' pattern/expression is matched with a 'Left' value.+ | PEIncompatibleUpdates+ -- ^ Occurrences of the same variable in an expression are matched with different values.+ | PEMultipleUpdates+ -- ^ A variable that should appear at most once in an expression is however used multiple times.+ | PEValueUnrecoverable+ -- ^ A variable that should appear at least once in an expression is however absent.+ | PEProdL PatError+ -- ^ The error is on the left of a product pattern/expression.+ | PEProdR PatError+ -- ^ The error is on the right of a product pattern/expression.+ | PELeft PatError+ -- ^ The error is inside a 'Left' pattern/expression.+ | PERight PatError+ -- ^ The error is inside a 'Right' pattern/expression.+ | PEIn PatError+ -- ^ The error is inside a constructor pattern/expression. -liftE :: (a -> b) -> Either a c -> Either b c-liftE f = either (Left . f) Right+instance Show PatError where+ show PEConstantMismatch = "matching a constant pattern/expression with a different value"+ show PELeftMismatch = "matching a Left pattern/expression with a Right value"+ show PERightMismatch = "matching a Right pattern/expression with a Left value"+ show PEIncompatibleUpdates = "matching occurrences of the same variable with different values"+ show PEMultipleUpdates = "multiple occurrences of a variable that can only be used at most once"+ show PEValueUnrecoverable = "no occurrences of a variable that should be used at least once"+ show (PEProdL e) = "on the left of a product pattern/expression\n" ++ show e+ show (PEProdR e) = "on the right of a product pattern/expression\n" ++ show e+ show (PELeft e) = "inside a Left pattern/expression\n" ++ show e+ show (PERight e) = "inside a Right pattern/expression\n" ++ show e+ show (PEIn e) = "inside a constructor pattern/expression\n" ++ show e
src/Generics/BiGUL/Interpreter.hs view
@@ -1,109 +1,170 @@-module Generics.BiGUL.Interpreter (put, get, PutResult, GetResult, errorTrace) where+-- | The standard interpreters, which perform all dynamic checks to ensure well-behavedness+-- and produce trace information when execution fails.+-- Currently, tracing is designed for debugging, and only the traces leading to failure+-- can be expected to contain a complete log of the steps executed.+-- In other words, traces leading to success usually contain only partial tracing information.+-- Also, when a program loops, there is no guarantee that the trace is computed productively.+-- Finally, /note that branch numbering starts from 0./ +module Generics.BiGUL.Interpreter (put, putTrace, get, getTrace) where++import Generics.BiGUL import Generics.BiGUL.Error-import Generics.BiGUL.AST-import Control.Monad.Except-import Text.PrettyPrint+import Generics.BiGUL.PatternMatching +import Control.Monad -errorTrace :: PrettyPrintable e => Either e a -> Either Doc a-errorTrace = either (Left . (text "error" $+$) . toDoc) Right -catchBind :: Either e a -> (a -> Either e b) -> (e -> Either e b) -> Either e b-catchBind ma f g = either g f ma+-- | A 'Maybe' monad with/modulo trace information —+-- the monad laws hold only when the trace component is /not/ considered.+newtype BiGULResult a = BiGULResult { runBiGULResult :: (Maybe a, BiGULTrace) } -type PutResult s v = Either (PutError s v) s+instance Functor BiGULResult where+ fmap = liftM -put :: BiGUL s v -> s -> v -> PutResult s v-put (Fail str) s v = throwError (PFail str)-put Skip s v = return s-put Replace s v = return v-put (Prod bigul bigul') (s, s') (v, v') = liftM2 (,) (liftE (PProdLeft s v ) (put bigul s v ))- (liftE (PProdRight s' v') (put bigul' s' v'))-put (RearrS pat expr bigul) s v = do env <- liftE PSourcePatternMismatch (deconstruct pat s)- let m = eval expr env- s' <- liftE (PRearrS m v) (put bigul m v)- con <- liftE PUnevalFailed (uneval pat expr s' (emptyContainer pat))- return (construct pat (fromContainerS pat env con))-put (RearrV pat expr bigul) s v = do v' <- liftE PViewPatternMismatch (deconstruct pat v)- let m = eval expr v'- liftE (PRearrV s m) (put bigul s m)-put (Dep bigul f) s (v, v') = do s' <- liftE (PDep s v) (put bigul s v)- if f s' v == v'- then return s'- else throwError (PDependencyMismatch s')-put (Case branches) s v = putCase branches s v-put (Compose bigul bigul') s v = do m <- liftE PNoIntermediateSource (get bigul s)- m' <- liftE (PComposeRight m v) (put bigul' m v)- liftE (PComposeLeft s m') (put bigul s m')+instance Applicative BiGULResult where+ pure x = BiGULResult (Just x, BTSuccess)+ (<*>) = ap -getCaseBranch :: (s -> v -> Bool, CaseBranch s v) -> s -> GetResult s v-getCaseBranch (p , Normal bigul q) s =+instance Monad BiGULResult where+ return = pure+ BiGULResult (Just x , t) >>= f = f x+ BiGULResult (Nothing, t) >>= f = BiGULResult (Nothing, t)++-- Auxiliary functions for 'BiGULResult'.++catchBind :: BiGULResult a -> (a -> BiGULResult b) -> (BiGULTrace -> BiGULResult b) -> BiGULResult b+catchBind (BiGULResult (Just x , _)) f g = f x+catchBind (BiGULResult (Nothing, t)) f g = g t++errorResult :: BiGULError -> BiGULResult a+errorResult e = BiGULResult (Nothing, BTError e)++modifyTrace :: (BiGULTrace -> BiGULTrace) -> BiGULResult a -> BiGULResult a+modifyTrace f (BiGULResult ~(mx, t)) = BiGULResult (mx, f t)++embedEither :: (e -> BiGULError) -> Either e a -> BiGULResult a+embedEither f = either (errorResult . f) return++incrBranchNo :: BiGULTrace -> BiGULTrace+incrBranchNo (BTBranch i t) = BTBranch (i+1) t+incrBranchNo t = t++addCurrentBranchTrace :: BiGULTrace -> BiGULTrace -> BiGULTrace+addCurrentBranchTrace t (BTBranches ts) = BTBranches (t:ts)+addCurrentBranchTrace t _ = error "panic: Generics.BiGUL.Error.addCurrentBranchTrace"++-- | The putback semantics of a 'Generics.BiGUL.BiGUL' program.+put :: BiGUL s v -> s -> v -> Maybe s+put b s v = fst (runBiGULResult (putWithTrace b s v))++-- | The execution trace of a 'put' computation.+putTrace :: BiGUL s v -> s -> v -> BiGULTrace+putTrace b s v = snd (runBiGULResult (putWithTrace b s v))++putWithTrace :: BiGUL s v -> s -> v -> BiGULResult s+putWithTrace (Fail str) s v = errorResult (BEFail str)+putWithTrace (Skip f) s v = if f s == v then return s else errorResult BESkipMismatch+putWithTrace Replace s v = return v+putWithTrace (l `Prod` r) (s, s') (v, v') =+ liftM2 (,) (modifyTrace (BTNextSV "on the left of Prod" s v ) (putWithTrace l s v ))+ (modifyTrace (BTNextSV "on the right of Prod" s' v') (putWithTrace r s' v'))+putWithTrace (RearrS p e b) s v = do+ env <- embedEither BESourcePatternMismatch (deconstruct p s)+ let m = eval e env+ s' <- modifyTrace (BTNextSV "inside RearrS" m v) (putWithTrace b m v)+ con <- embedEither BEInvRearrFailed (uneval p e s' (emptyContainer p))+ return (construct p (fromContainerS p env con))+putWithTrace (RearrV p e b) s v = do+ v' <- embedEither BEViewPatternMismatch (deconstruct p v)+ let m = eval e v'+ modifyTrace (BTNextSV "inside RearrV" s m) (putWithTrace b s m)+putWithTrace (Dep f b) s (v, v') =+ if f v == v' then modifyTrace (BTNextSV "inside Dep" s v) (putWithTrace b s v)+ else errorResult BEDependencyMismatch+putWithTrace (Case bs) s v = putCase bs s v+putWithTrace (l `Compose` r) s v = do+ m <- modifyTrace (BTNextS "computing intermediate source" s) (getWithTrace l s)+ m' <- modifyTrace (BTNextSV "on the right of Compose" m v) (putWithTrace r m v)+ modifyTrace (BTNextSV "on the left of Compose" s m') (putWithTrace l s m')++getCaseBranch :: (s -> v -> Bool, CaseBranch s v) -> s -> BiGULResult v+getCaseBranch (p, Normal b q) s = if q s- then do v <- get bigul s+ then do v <- getWithTrace b s if p s v then return v- else throwError GPostVerificationFailed- else throwError GBranchUnmatched-getCaseBranch (p , Adaptive f) s = throwError GAdaptiveBranchMatched+ else errorResult BEPostVerificationFailed+ else errorResult BEBranchUnmatched+getCaseBranch (p, Adaptive f) s = errorResult BEAdaptiveBranchMatched -putCaseCheckDiversion :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v -> Either (PutError s v) ()+putCaseCheckDiversion :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v -> BiGULResult () putCaseCheckDiversion [] s v = return () putCaseCheckDiversion (pb@(p, b):bs) s v = if not (p s v)- then catchBind (liftE (const undefined) (getCaseBranch pb s))- (const (throwError PPreviousBranchMatched))- (const (putCaseCheckDiversion bs s v))- else throwError PPreviousBranchMatched+ then catchBind (getCaseBranch pb s) (const (errorResult BEPreviousBranchMatched))+ (const (putCaseCheckDiversion bs s v))+ else errorResult BEPreviousBranchMatched putCaseWithAdaptation :: [(s -> v -> Bool, CaseBranch s v)] -> [(s -> v -> Bool, CaseBranch s v)] ->- s -> v -> (s -> PutResult s v) -> PutResult s v-putCaseWithAdaptation [] bs' s v cont = throwError PCaseExhausted+ s -> v -> (s -> BiGULResult s) -> BiGULResult s+putCaseWithAdaptation [] bs' s v cont = errorResult BECaseExhausted putCaseWithAdaptation (pb@(p, b):bs) bs' s v cont = if p s v- then liftE (PBranch 0) $+ then modifyTrace (BTBranch 0) $ case b of- Normal bigul q -> do- s' <- put bigul s v+ Normal b q -> do+ s' <- putWithTrace b s v if p s' v then if q s' then putCaseCheckDiversion bs' s' v >> return s'- else throwError PBranchPredictionIncorrect- else throwError PPostVerificationFailed+ else errorResult BEExitConditionFailed+ else errorResult BEPostVerificationFailed Adaptive f -> cont (f s v)- else liftE incrBranchNo (putCaseWithAdaptation bs (pb:bs') s v cont)+ else modifyTrace incrBranchNo (putCaseWithAdaptation bs (pb:bs') s v cont) -putCase :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v -> Either (PutError s v) s+putCase :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v -> BiGULResult s putCase bs s v = putCaseWithAdaptation bs [] s v (\s' -> putCaseWithAdaptation bs [] s' v- (const (throwError PAdaptiveBranchRevisited)))+ (const (errorResult BEAdaptiveBranchRevisited))) -type GetResult s v = Either (GetError s v) v+-- | The get semantics of a 'Generics.BiGUL.BiGUL' program.+get :: BiGUL s v -> s -> Maybe v+get b s = fst (runBiGULResult (getWithTrace b s)) -get :: BiGUL s v -> s -> GetResult s v-get (Fail str) s = throwError (GFail str)-get Skip s = return ()-get Replace s = return s-get (Prod bigul bigul') (s, s') = liftM2 (,) (liftE (GProdLeft s ) (get bigul s ))- (liftE (GProdRight s') (get bigul' s'))-get (RearrS pat expr bigul) s = do env <- liftE GSourcePatternMismatch (deconstruct pat s)- let m = eval expr env- liftE (GRearrS m) (get bigul m)-get (RearrV pat expr bigul) s = do v' <- liftE (GRearrV s) (get bigul s)- con <- liftE GUnevalFailed (uneval pat expr v' (emptyContainer pat))- env <- liftE GViewRecoveringIncomplete (fromContainerV pat con)- return (construct pat env)-get (Dep bigul f) s = do v <- liftE (GDep s) (get bigul s)- return (v, f s v)-get (Case branches) s = getCase branches s-get (Compose bigul bigul') s = do m <- liftE (GComposeLeft s) (get bigul s)- liftE (GComposeRight m) (get bigul' m)+-- | The execution trace of a 'get' computation.+getTrace :: BiGUL s v -> s -> BiGULTrace+getTrace b s = snd (runBiGULResult (getWithTrace b s)) -getCase :: [(s -> v -> Bool, CaseBranch s v)] -> s -> GetResult s v-getCase [] s = throwError (GCaseExhausted [])+getWithTrace :: BiGUL s v -> s -> BiGULResult v+getWithTrace (Fail str) s = errorResult (BEFail str)+getWithTrace (Skip f) s = return (f s)+getWithTrace Replace s = return s+getWithTrace (l `Prod` r) (s, s') =+ liftM2 (,) (modifyTrace (BTNextS "on the left of Prod" s ) (getWithTrace l s ))+ (modifyTrace (BTNextS "on the right of Prod" s') (getWithTrace r s'))+getWithTrace (RearrS p e b) s = do+ env <- embedEither BESourcePatternMismatch (deconstruct p s)+ let m = eval e env+ modifyTrace (BTNextS "inside RearrS" m) (getWithTrace b m)+getWithTrace (RearrV p e b) s = do+ v' <- modifyTrace (BTNextS "inside RearrV" s) (getWithTrace b s)+ con <- embedEither BEInvRearrFailed (uneval p e v' (emptyContainer p))+ env <- embedEither BEViewRecoveringFailed (fromContainerV p con)+ return (construct p env)+getWithTrace (Dep f b) s = do+ v <- modifyTrace (BTNextS "inside Dep" s) (getWithTrace b s)+ return (v, f v)+getWithTrace (Case bs) s = getCase bs s+getWithTrace (l `Compose` r) s = do+ m <- modifyTrace (BTNextS "on the left of Compose" s) (getWithTrace l s)+ modifyTrace (BTNextS "on the right of Compose" m) (getWithTrace r m)++getCase :: [(s -> v -> Bool, CaseBranch s v)] -> s -> BiGULResult v+getCase [] s = BiGULResult (Nothing, BTBranches []) getCase (pb@(p, b):bs) s = catchBind (getCaseBranch pb s) return- (\e -> do v <- liftE (addCurrentBranchError e) (getCase bs s)+ (\t -> do v <- modifyTrace (addCurrentBranchTrace t) (getCase bs s) if not (p s v) then return v- else throwError (GBranch 0 GPreviousBranchMatched))+ else modifyTrace (BTBranch 0) (errorResult BEPreviousBranchMatched))
src/Generics/BiGUL/Interpreter/Unsafe.hs view
@@ -1,67 +1,71 @@+-- | The unsafe interpreters, which assume that computation always succeeds and omit all dynamic checking.+-- Use these interpreters only when you have ensured that your 'Generics.BiGUL.BiGUL' program is correct.+ module Generics.BiGUL.Interpreter.Unsafe (put, get) where -import Generics.BiGUL.AST+import Generics.BiGUL+import Generics.BiGUL.PatternMatching fromRight :: Either a b -> b fromRight (Right b) = b fromRight _ = error "fromRight fails" +-- | The unsafe putback semantics of a 'Generics.BiGUL.BiGUL' program. put :: BiGUL s v -> s -> v -> s-put (Fail err) s v = error ("fail: " ++ err)-put Skip s v = s-put Replace s v = v-put (Prod bigul bigul') (s, s') (v, v') = (put bigul s v, put bigul' s' v')-put (RearrS pat expr bigul) s v = let env = fromRight (deconstruct pat s)- m = eval expr env- s' = put bigul m v- con = fromRight (uneval pat expr s' (emptyContainer pat))- in construct pat (fromContainerS pat env con)-put (RearrV pat expr bigul) s v = let v' = fromRight (deconstruct pat v)- m = eval expr v'- in put bigul s m-put (Dep bigul f) s (v, v') = put bigul s v-put (Case branches) s v = putCase branches s v-put (Compose bigul bigul') s v = let m = get bigul s- m' = put bigul' m v- in put bigul s m'+put (Fail str) s v = error ("fail: " ++ str)+put (Skip f) s v = s+put Replace s v = v+put (l `Prod` r) (s, s') (v, v') = (put l s v, put r s' v')+put (RearrS p e b) s v = let env = fromRight (deconstruct p s)+ m = eval e env+ s' = put b m v+ con = fromRight (uneval p e s' (emptyContainer p))+ in construct p (fromContainerS p env con)+put (RearrV p e b) s v = let v' = fromRight (deconstruct p v)+ m = eval e v'+ in put b s m+put (Dep f b) s (v, v') = put b s v+put (Case bs) s v = putCase bs s v+put (l `Compose` r) s v = let m = get l s+ m' = put r m v+ in put l s m' getCaseBranch :: (s -> v -> Bool, CaseBranch s v) -> s -> Maybe v-getCaseBranch (p , Normal bigul q) s =+getCaseBranch (p , Normal b q) s = if q s- then let v = get bigul s+ then let v = get b s in if p s v then Just v else Nothing else Nothing-getCaseBranch (p , Adaptive f) s = Nothing+getCaseBranch (p , Adaptive f) s = Nothing putCaseWithAdaptation :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v -> (s -> s) -> s putCaseWithAdaptation (pb@(p, b):bs) s v cont = if p s v then case b of- Normal bigul q -> put bigul s v+ Normal b q -> put b s v Adaptive f -> cont (f s v) else putCaseWithAdaptation bs s v cont putCase :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v -> s putCase bs s v = putCaseWithAdaptation bs s v (\s' -> putCase bs s' v) +-- | The unsafe get semantics of a 'Generics.BiGUL.BiGUL' program. get :: BiGUL s v -> s -> v-get (Fail err) s = error ("fail: " ++ err)-get Skip s = ()-get Replace s = s-get (Prod bigul bigul') (s, s') = (get bigul s, get bigul' s')-get (RearrS pat expr bigul) s = let env = fromRight (deconstruct pat s)- m = eval expr env- in get bigul m-get (RearrV pat expr bigul) s = let v' = get bigul s- con = fromRight (uneval pat expr v' (emptyContainer pat))- env = fromRight (fromContainerV pat con)- in construct pat env-get (Dep bigul f) s = let v = get bigul s- in (v, f s v)-get (Case branches) s = getCase branches s-get (Compose bigul bigul') s = let m = get bigul s- in get bigul' m+get (Fail str) s = error ("fail: " ++ str)+get (Skip f) s = f s+get Replace s = s+get (l `Prod` r) (s, s') = (get l s, get r s')+get (RearrS p e b) s = let env = fromRight (deconstruct p s)+ m = eval e env+ in get b m+get (RearrV p e b) s = let v' = get b s+ con = fromRight (uneval p e v' (emptyContainer p))+ env = fromRight (fromContainerV p con)+ in construct p env+get (Dep f b) s = let v = get b s in (v, f v)+get (Case bs) s = getCase bs s+get (l `Compose` r) s = let m = get l s in get r m getCase :: [(s -> v -> Bool, CaseBranch s v)] -> s -> v getCase (pb@(p, b):bs) s =
+ src/Generics/BiGUL/Lib.hs view
@@ -0,0 +1,26 @@+-- | A prelude for BiGUL programs.++module Generics.BiGUL.Lib where++import Generics.BiGUL+import Generics.BiGUL.TH+++-- | Skip updating the source when the view is known to be a constant. Same as @Skip . const@.+skip :: Eq v => v -> BiGUL s v+skip v = Skip (const v)++-- | A nicer notation for applying a branch constructing function to a branch body. Same as 'Prelude.$'.+(==>) :: (a -> b) -> a -> b+(==>) = ($)++infixr 0 ==>++-- | Embed a well-behaved pair of transformations into BiGUL.+emb :: Eq v => (s -> v) -> (s -> v -> s) -> BiGUL s v+emb g p = Case+ [ $(normal [| \s v -> g s == v |] [p| _ |])+ ==> Skip g+ , $(adaptive [| \s v -> {- g s /= v -} True |])+ ==> p+ ]
+ src/Generics/BiGUL/Lib/HuStudies.hs view
@@ -0,0 +1,468 @@+-- | The module contains many examples, from easy to difficult, showing how to program in BiGUL.++module Generics.BiGUL.Lib.HuStudies where++import Generics.BiGUL+import Generics.BiGUL.Interpreter+import Generics.BiGUL.TH++-- |+-- > alwaysFail = Fail "always fail"+-- the combinator 'Fail' will always fail all the transformation, reporting the given error message.+--+-- >>> get alwaysFail "Please succeed"+-- Left fail: always fail+-- >>> put alwaysFail 23 False+-- Left fail: always fail+alwaysFail :: BiGUL a b+alwaysFail = Fail "always fail"+++-- |+-- > constSquare = Skip (\s -> s * s)+-- (Skip f) means that in the get direction, the view is fully computed by apply function f to the source.+-- So in the put direction, the update is skipped and the source is unchanged.+-- in the put direction, if (f source) /= view, an error is raised.+-- the view is the square of the source.+--+-- >>> put constSquare 10 100+-- Right 10+--+-- >>> put constSquare 10 225+-- Left *** Exception blabla...+--+-- >>> get constSquare 5+-- Right 25+constSquare :: BiGUL Int Int+constSquare = Skip (\s -> s * s)+++-- |+-- > repFirst = Replace `Prod` Skip (const ())+-- the example shows a simplest case to chain basic constructors of BiGUL together.+-- 'Prod' is right associative with priority 1 (0 is the lowest, e.g. $ is 0)+-- To use 'Prod', the source and view are assumed to be tuple.+-- the first elements of both tuples are associated by 'Replace', the second by 'Skip'.+--+-- >>> put repFirst (False, 9) (True, ())+-- Right (True,9)+--+-- >>> get repFirst (True, 3)+-- Right (True,())+repFirst :: (Show a, Show b) => BiGUL (a, b) (a, ())+repFirst = Replace `Prod` Skip (const ())+++-- |+-- > repFirst' = $(update [p| (x,_) |] [p| (x,()) |] [d| x = Replace |])+-- This is the 'repFirst' example rewritten with syntactic sugar.+-- The syntax is+--+-- > $(update [p| source-pattern |] [p| view-pattern |] [d| updating-strategy |])+-- Source and view are decomposed by the patterns in the [p| ... |].+-- In this concrete example, the first elements of the tuple (both in the source and in the viwe) are bound to variable x,+-- and they are sent to the combinator 'Replace' as arguments by (/x=Replace/) in the [d| ... |] part.+-- anything we want to perform 'Skip' should be marked as underline(_) in the [p| source-pattern |] and it should not appear in the [d| ... |] part.+--+-- The source-pattern and view-pattern can be very different, for example:+--+-- > $([p| Left x : _ |] [p| ((), x) |] [d| x = blabla... |])+-- where the source-pattern stands for a non-empty list of Either type, and we bind variable x to the inner part of the Left constructor.+-- However the view is a tuple, and we bind the second element to x.+-- The rearrangement for source and view is automatically done.+repFirst' :: Show a => BiGUL (a, b) (a, ())+repFirst' = $(update [p| (x,_) |] [p| (x,()) |] [d| x = Replace |])++++-- |+-- > repFirstV2 = RearrV PVar' (EDir DVar `EProd` EConst ()) repFirst+-- Skip to 'repFirstV2'' if you want to use the syntactic sugar only.+--+-- In the previous example, suppose what we really want is that, the view has type a rather than (a,()).+-- In order to do so, we can use 'RearrV', which first rearranges the view into the desired structure,+-- and then uses another BiGUL program to perform the transformation.+--+-- The usage of 'RearrV' is:+--+-- > RearrV (old-pattern) (new-pattern) (bigul-program)+--+-- 'PVar' means there is a variable (a hole to be update), and 'PVar'' is the same as 'PVar' except that it does not require the 'Eq' constraint.+-- In this concrete example, since the view is a single variable, the /old-pattern/ is 'PVar''.+-- 'DVar' still means there is a variable. But DVar should be used in the second argument of the 'RearrV'.+-- Since there may be many variables in the old-pattern, we need to mark the origin of each variable in the new-pattern: where does it come from.+-- In this concrete example, because there is only one variable in the old-pattern, a 'DVar' is enough to locate it.+-- Then we use 'EDir' to make 'DVar' becomes a direction, 'EProd' it with a constant 'EConst' ().+-- A more complex situation about the new-pattern is in the 'repFirstV3' example.++--+-- >>> put repFirstV2 (undefined , Nothing) 3+-- Right (3,Nothing)+--+-- >>> get repFirstV2 (True, undefined)+-- Right True+repFirstV2 :: (Show a, Show b) => BiGUL (a,b) a+repFirstV2 = RearrV PVar' (EDir DVar `EProd` EConst ()) repFirst++++-- |+-- > repFirstV2' = $(rearrV [| \v -> (v,()) |]) repFirst+-- It is difficult to use primitive combinators, especially when the problem becomes complex.+-- Here we introduce another syntactic sugar:+--+-- > $(rearrV [| \old-pattern -> new-pattern |]) bigul-program+-- When rearranging the view, it's possible to duplicate information:+--+-- > $(rearrV [| \v -> (v,v)|]) bigul-program+-- but it's __/not allowed/__ to drop information:+--+-- > $(rearrV [| \(vl,vr) -> vl |]) bigul-program -- this is WRONG+repFirstV2' :: (Show a, Show b) => BiGUL (a,b) a+repFirstV2' = $(rearrV [| \v -> (v,()) |]) repFirst++++-- |+-- > repFirstV3 = RearrS (PVar' `PProd` PVar') (EDir (DLeft DVar)) Replace+-- The function produces exactly the same result as 'repFirstV2'.+-- The difference is that, instead of using 'RearrV' to rearrange the view into a tuple,+-- here we use 'RearrS' to rearrange the source into a single value of type a, and then simply use 'Replace' to update the source.+--+-- The usage of 'RearrS' is:+--+-- > RearrS (old-pattern) (new-pattern) (bigul-program)+-- The original source is a tuple, that is to say, there are two variables. So the old-pattern is (PVar' \`Prod\` PVar').+-- In the new-pattern, We need to tell BiGUL that, it is the first element of the tuple, i.e. the left element, we want to update.+-- This is achieved by (DLeft DVar). Finally, we convert it into a direction using 'EDir'.+repFirstV3 :: Show a => BiGUL (a,b) a+repFirstV3 = RearrS (PVar' `PProd` PVar') (EDir (DLeft DVar)) Replace+++-- |+-- > repFirstV3' = $(rearrS [| \(l, _) -> l |]) Replace+-- The syntactic sugar version for 'repFirstV3'.+-- The usage of the syntactic sugar is basically the same as $(rearrV ...):+--+-- > $(rearrS [| \old-pattern -> new-pattern |]) bigul-program+repFirstV3' :: Show a => BiGUL (a,b) a+repFirstV3' = $(rearrS [| \(l, _) -> l |]) Replace+++-- |+-- > repFirstV4 = Dep (const ()) ($(rearrS [| \(l, _) -> l |]) Replace)+-- Yet another version of 'repFirst'. This artificial example briefly introduces the constructor 'Dep', which is rather seldom used.+-- 'Dep' can be used to add or eliminate information on the view.+-- In this concrete example, since the second element of the view is a unit (()), it can be produced from any existing view v by+-- (const ()), which is equivalent to (\v -> const () v).+-- Now we can consider a bidirectional transformation f+--+-- > ($(rearrS [| \(l, _) -> l |]) Replace)+-- which is between source (a,b) and view a only.+-- Then both the transformation f and the function (const ()) are passed to 'Dep' to finally produce the transformation between (a, b) and (a, ())+repFirstV4 :: (Show a, Show b) => BiGUL (a, b) (a, ())+repFirstV4 = Dep (const ()) ($(rearrS [| \(l, _) -> l |]) Replace)++++-- |+-- > Case [ $(normal [| \(a,b) c -> a <= b && c <= b |]+-- > [|\(a,b) -> a <= b |])+-- > $(update [p| (a,_) |] [p| a |] [d| a = Replace |])+-- > , $(normal [| \(a,b) c -> b < a && c < a |]+-- > [|\(a,b) -> b < a |])+-- > $(update [p| (_,b) |] [p| b |] [d| b = Replace |])+-- > , $(normal [|\ _ _ -> True|]+-- > [|\(a,b) -> False |])+-- > (Fail "the source view are not consistent.")+-- > ]+-- Here we introduce the 'Case' combinator, which is extremely useful. 'Case' resembles the conditional branch in most languages.+-- In this concrete example, the 'Case' combinator enables us to do the following: Suppose the source is (a,b) and the view is c,+-- if (a <= b && c <= b), then we replace a with c; if (b < a && c < a), then we replace b with c; otherwise, the program fails.+-- Because in this case once the minimum element in the source is replaced, it is no longer the minimum element.+--+-- The general structure for 'Case' is:+--+-- > Case [ $(normal [| enteringCond1 :: s -> v -> Bool |] [|exitCond1 :: s -> Bool |]) $+-- > (bx1 :: BiGUL s v)+-- > , $(adaptive [| enteringCond1' :: s -> v -> Bool |]) $+-- > (f1 :: s -> v -> s)+-- > , ...+-- > , $(normal [| enteringCondn :: s -> v -> Bool |] [|exitCond1 :: s -> Bool |]) $+-- > (bxn :: BiGUL s v)+-- > , ...+-- > , $(adaptive [| enteringCondm' :: s -> v -> Bool |]) $+-- > (fm :: s -> v -> s)+-- > ]+-- > :: BiGUL s v+--+-- It contains a sequence of cases. For each case, it is either normal or adaptive. For+-- the normal case, if the condition is satisfied, a corresponding putback transformation+-- is applied. For the adaptive case, if the condition is satisfied, a function+-- is used to update the source with the view so that for the next step one of the+-- normal cases can be applied. Note that if adaptation does not lead the source+-- and the view to a normal case, an error will be reported at runtime.+-- The example for /adaptive/ branch is in the next example.+--+-- Note that $(normal ... ...) takes two predicates. The first one is the entering-condition while the second one is the exit-condition.+-- The predicate for entering-condition is very general, and we can use any function f of type (s -> v -> Bool) to examine the source and view.+-- If the condition is matched, then the BiGUL program after the predicate is executed. If the condition is not satisfied, the next branch is tried.+-- The predicate for exit-condition checks the source only. The exit-condition in different branches should be always NOT overlapped.+-- Eg: (a <= b), (b < a), (False) are not overlapped.+--+-- Note: instead of a general function, we can use patterns for predicate. The syntax is:+--+-- > $(normalSV [p| source-pattern |] [p| view-pattern |] [| exitCond |] )+-- > ...+-- > $(adaptiveSV [p| source-pattern |] [p| view-pattern |])+-- For example:+--+-- > $(normalSV [p| Left _:_ |] [p| [] |]+-- > [| exitCond |] )+-- states that the source is a non-empty list with the first element in a /Left/ constructor,+-- and the view is an empty list. This feature is heavily used in the 'naiveMap' example.+--+-- Please avoid using variables in the pattern-predicate: always use an underline.+--+-- >>> put replaceMin (2,7) 4+-- Right (4,7)+--+-- >>> put replaceMin (2,7) 10+-- Left fail: the source view are not consistent.+--+-- >>> get replaceMin (2,7)+-- Right 2+replaceMin :: BiGUL (Int, Int) Int+replaceMin =+ Case [ $(normal [| \(a,b) c -> a <= b && c <= b |]+ [|\(a,b) -> a <= b |])+ $(update [p| (a,_) |] [p| a |] [d| a = Replace |])+ , $(normal [| \(a,b) c -> b < a && c < a |]+ [|\(a,b) -> b < a |])+ $(update [p| (_,b) |] [p| b |] [d| b = Replace |])+ , $(normal [|\ _ _ -> True|]+ [|\(a,b) -> False |])+ (Fail "the source view are not consistent.")+ ]++++-- |+-- > lensLength def =+-- > Case [ $(adaptive [| \s v -> length s /= v |])+-- > (\s v -> let ls = length s+-- > in if ls > v then drop (ls - v) s+-- > else replicate (v - ls) def ++ s)+-- > , $(normal [|\s v -> length s == v |]+-- > [| const True |])+-- > (Skip length)+-- > ]+-- In this example, the source is any list and the view is the length of the source.+-- Note that The function is not a lens: we should provide the function with a default value to make it a lens.+-- The default value is used to generate new elements and thus expand the source, when the view is greater than the length of the source.+-- If the view is less than the length of the source, the source will be shortened.+--+-- Here we introduce the adaptive branch of 'Case', which takes a predicate (just like 'Normal' branch),+-- and a function (f :: s -> v -> s) that is used to create a new source.+-- Adaptive branch can be placed anywhere in a 'Case'.+-- Once the adaptive branch is executed and the new source is created, the whole 'Case' will be re-executed from the first branch.+-- If again an adaptive branch is matched, an error is thrown.+--+-- Another point is that, adaptive branch is chosen in the put direction only. In the get direction, it will never be chosen.+--+-- >>> put (lensLength 10) [2,2,1] 2+-- Right [2,1]+--+-- >>> put (lensLength 10) [2,2,1] 6+-- Right [10,10,10,2,2,1]+--+-- >>> get (lensLength undefined) [1..10]+-- Right 10+lensLength :: a -> BiGUL [a] Int+lensLength def =+ Case [ $(adaptive [| \s v -> length s /= v |])+ (\s v -> let ls = length s+ in if ls > v then drop (ls - v) s+ else replicate (v - ls) def ++ s)+ , $(normal [|\s v -> length s == v |]+ [| const True |])+ (Skip length)+ ]+++-- |+-- > lensLength' def = emb length p+-- > where p = \s v -> let ls = length s+-- > in if ls > v then drop (ls - v) s+-- > else replicate (v - ls) def ++ s+-- In fact what lensLength expresses is just that: We have two functions g and p,+-- g is used to do the get (by a 'Normal' branch), while p is used to do the put (by an 'Adaptive' branch).+-- this intention can be expressed in a more simple and modular way: using the 'emb' (embed) function.+-- the definition of 'emb' can be found in the next example.+lensLength' :: a -> BiGUL [a] Int+lensLength' def = emb length p+ where p = \s v -> let ls = length s+ in if ls > v then drop (ls - v) s+ else replicate (v - ls) def ++ s+++-- |+-- > (==>) = ($)+-- make it more elegant to write ($). Later we may use (==>) instead of ($).+(==>) :: (a -> b) -> a -> b+(==>) = ($)++-- |+-- > emb g p = Case+-- > [ $(normal [| \s v -> g s == v |] [p| _ |])+-- > ==> Skip g+-- > , $(adaptive [| \s v -> True |])+-- > ==> p+-- > ]+-- emb g p: invoke g to do the get, and invoke p to do the put.+emb :: Eq v => (s -> v) -> (s -> v -> s) -> BiGUL s v+emb g p = Case+ [ $(normal [| \s v -> g s == v |] [p| _ |])+ ==> Skip g+ , $(adaptive [| \s v -> True |])+ ==> p+ ]+++-- |+-- > lensSucc = emb (flip (+) 1) (\_ v -> v - 1)+-- Sometimes 'emb' is useful. For instance, Int is a primitive datatype without any constructor in Haskell,+-- and cannot be manipulated in a way like list in 'ReaarV' or 'RearrS'.+-- For list, we can write (x:xs) -> (x:x:xs) using its constructor. But we cannot decompose Int.+-- Making use of 'emb', we can manipulate basic operations for Int, whose well-behavedness should be proved by hand.+-- (But here, the well-behavedness is easily seen.)+--+-- >>> put lensSucc 0 10+-- Right 9+--+-- >>> get lensSucc 8+-- Right 9+lensSucc :: BiGUL Int Int+lensSucc = emb (flip (+) 1) (\_ v -> v - 1)++-- |+-- > naiveMap b =+-- > Case [ $(normalSV [p| _:_ |] [p| _:_ |]+-- > [p| _:_ |])+-- > ==> $(update [p| x:xs |] [p| x:xs |] [d| x = b; xs = naiveMap b |])+-- > , $(adaptiveSV [p| _:_ |] [p| [] |] ) (\_ _ -> [])+-- > , $(normalSV [p| [] |] [p| _:_ |]+-- > [| const False |])+-- > ==> (Fail "length of the view should be less than that of the source.")+-- > , $(normalSV [p| [] |] [p| [] |]+-- > [p| [] |])+-- > ==> $(update [p| [] |] [p| [] |] [d| |])+-- > ]+-- A naive map function, which takes a BiGUL program and yields another BiGUL program working on list.+-- The first branch deals with recursive condition.+-- The second branch handles the boundary conditions where the source list is longer than the view list:+-- drop all the remaining elements in the source list and thus make it an empty list.+-- The third branch will throw an error when the view list is longer than the source list.+-- The last branch is the termination condition: both the source and view reach the empty constructor.+--+-- (For the sake of completeness.) In fact 'normalSV' means that we use separate condition for source and view.+-- So we can still use a general function in the predicate:+--+-- > $(normalSV [| \s -> case s of _:_ -> True; _ -> False |] [p| _:_ |] [p| _:_ |])+--+-- >>> put (naiveMap lensSucc) [1,2,3,4] [7,8,9]+-- Right [6,7,8]+--+-- >>> get (naiveMap (lensLength undefined)) ["123", "xyz"]+-- Right [3,3]+--+-- >>>get (naiveMap replaceMin) [(3,9), (-2,10),(10,2)]+-- Right [3,-2,2]++naiveMap :: (Show a, Show b) => BiGUL a b -> BiGUL [a] [b]+naiveMap b =+ Case [ $(normalSV [p| _:_ |] [p| _:_ |]+ [p| _:_ |])+ ==> $(update [p| x:xs |] [p| x:xs |] [d| x = b; xs = naiveMap b |])+ , $(adaptiveSV [p| _:_ |] [p| [] |] ) (\_ _ -> [])+ , $(normalSV [p| [] |] [p| _:_ |]+ [| const False |])+ ==> (Fail "length of the view should be less than that of the source.")+ , $(normalSV [p| [] |] [p| [] |]+ [p| [] |])+ ==> $(update [p| [] |] [p| [] |] [d| |])+ ]++-- |+-- > compose = Compose+-- The last combinator we are going to introduce is 'Compose',+-- which takes two BiGUL programs and behaves like \"function composition\".+--+-- Given two BiGUL programs,+--+-- > f :: BiGUL a b, g :: BiGUL b c+-- we have+--+-- > f `Compose` g :: BiGUL a c+-- In the get direction, the semantics of @get (f \`Compose\` g) s@ is:+-- (suppose the function 'get' and 'put' always return a value rather than a value wrapped in 'Right'.)+--+-- > get g (get f s)+-- In the put direction, the semantics of @put (f \`Compose\` g) s v@ is a little bit complex:+--+-- > put f s (put g (get f s) v)+-- Let us make it more clear:+--+-- > let a = get f s+-- > b = put g a v+-- > in put f s b+-- Check the type of these transformations by yourself will help you understand deeper.+--+-- Let us try some examples:+--+-- >>> put ((naiveMap replaceMin) `compose` (naiveMap lensSucc)) [(1,-1),(-2,2)] [-8, 1]+-- Right [(1,-9),(0,2)]+--+-- >>> get ((naiveMap replaceMin) `compose` (naiveMap lensSucc)) [(1,-1),(-2,2)]+-- Right [0,-1]+compose :: (Show a, Show b, Show c) => BiGUL a b -> BiGUL b c -> BiGUL a c+compose = Compose++-- |+-- The last example in this tutorial is a simple map-map fusion.+-- It makes the composition of two map functions run more efficiently, compared to using 'Compose' combinator.+--+-- In the get direction, (get (f \`Compose\` g)) traverse the list twice, while (get (mapFusion f g)) traverse the list only once.+-- And in the put direction, (put f \`Compose\` g) traverse the two lists up to five times (get counts up once, two put count up four times, since a put takes two lists as argument),+-- while (put mapFusion f g) traverses the lists only twice.+--+-- Compare the following result (in GHCI)+--+-- > t1 :: Int+-- > t1 = last $ fromRight $ put (naiveMap lensSucc `Compose` naiveMap lensSucc) [1..100000] [2..20001]+-- > t2 :: Int+-- > t2 = last $ fromRight $ put (mapFusion lensSucc lensSucc) [1..100000] [2..20001]+-- > fromRight (Right x) = x+--+-- >>> t1+-- 19999+-- (1.24 secs, 512,471,456 bytes)+--+-- >>> t2+-- 19999+-- (0.23 secs, 122,920,792 bytes)+--+-- More examples can be found in the list library of BiGUL.+mapFusion :: (Show a, Show b, Show c) => BiGUL a b -> BiGUL b c -> BiGUL [a] [c]+mapFusion f g =+ Case [ $(normalSV [p| _:_ |] [p| _:_ |]+ [p| _:_ |])+ ==> $(update [p| x:xs |] [p| x:xs |] [d| x = f `Compose` g; xs = mapFusion f g |])+ , $(adaptiveSV [p| _:_ |] [p| [] |] ) (\_ _ -> [])+ , $(normalSV [p| [] |] [p| _:_ |]+ [| const False |])+ ==> (Fail "length of the view should be less than that of the source")+ , $(normalSV [p| [] |] [p| [] |]+ [p| [] |])+ ==> $(update [p| [] |] [p| [] |] [d| |])+ ]
+ src/Generics/BiGUL/Lib/List.hs view
@@ -0,0 +1,51 @@+-- | A library for processing lists in BiGUL.++module Generics.BiGUL.Lib.List where++import Generics.BiGUL+import Generics.BiGUL.TH+import Generics.BiGUL.Lib++import Control.Arrow ((***))+import Data.Maybe (isJust, catMaybes)+++-- | List alignment. Operating only on the sources satisfying the source condition,+-- and using the specified matching condition, 'align' finds for each view the first matching source+-- that has not been matched with previous views, and updates the source using the inner program.+-- If there is no matching source, one is created using the creation argument —+-- after creation, the created source should match with the view as determined by the matching condition.+-- For a source not matched with any view, the concealment argument is applied —+-- if concealment computes to @Nothing@, the source is deleted;+-- if concealment computes to @Just s'@, where @s'@ should not satisfy the source condition,+-- the source is replaced by @s'@.+align :: (Show a, Show b)+ => (a -> Bool) -- ^ source condition+ -> (a -> b -> Bool) -- ^ matching condition+ -> BiGUL a b -- ^ inner program+ -> (b -> a) -- ^ creation+ -> (a -> Maybe a) -- ^ concealment+ -> BiGUL [a] [b]+align p match b create conceal = Case+ [ $(normalSV [| null . filter p |] [p| [] |] [| null . filter p |])+ ==> $(rearrV [| \[] -> () |])$+ skip ()+ , $(adaptiveSV [p| _ |] [p| [] |])+ ==> \ss _ -> catMaybes (map (\s -> if p s then conceal s else Just s) ss)+ -- view is necessarily nonempty in the cases below+ , $(normalSV [p| (p -> False):_ |] [p| _ |] [p| (p -> False):(null . filter p -> False) |])+ ==> $(rearrS [| \(s:ss) -> ss |])$+ align p match b create conceal+ , $(normal [| \(s:ss) (v:vs) -> p s && match s v |] [p| (p -> True):_ |])+ ==> $(update [p| x:xs |] [p| x:xs |] [d| x = b; xs = align p match b create conceal |])+ , $(adaptive [| \ss (v:_) -> isJust (findFirst (\s -> p s && match s v) ss) ||+ let s = create v in p s && match s v |])+ ==> \ss (v:_) -> maybe (create v:ss) (uncurry (:)) (findFirst (\s -> p s && match s v) ss)+ ]+ where+ findFirst :: (a -> Bool) -> [a] -> Maybe (a, [a])+ findFirst p [] = Nothing+ findFirst p (x:xs) | p x = Just (x, xs)+ findFirst p (x:xs) | otherwise = fmap (id *** (x:)) (findFirst p xs)++
+ src/Generics/BiGUL/PatternMatching.hs view
@@ -0,0 +1,104 @@+-- | This module implements the rearrangement operations, which are based on pattern matching.++module Generics.BiGUL.PatternMatching where++import Generics.BiGUL+import Generics.BiGUL.Error++import GHC.InOut++import Control.Monad.Except+++modifyError :: (e -> e) -> Either e a -> Either e a+modifyError f = either (Left . f) Right++deconstruct :: Pat a env con -> a -> Either PatError env+deconstruct PVar x = return (Var x)+deconstruct PVar' x = return (Var x)+deconstruct (PConst c) x = if c == x then return () else throwError PEConstantMismatch+deconstruct (l `PProd` r) (x, y) = liftM2 (,) (modifyError PEProdL (deconstruct l x))+ (modifyError PEProdR (deconstruct r y))+deconstruct (PLeft p) (Left x) = modifyError PELeft (deconstruct p x)+deconstruct (PLeft _) _ = throwError PELeftMismatch+deconstruct (PRight p) (Right x) = modifyError PERight (deconstruct p x)+deconstruct (PRight _) _ = throwError PERightMismatch+deconstruct (PIn p) x = modifyError PEIn (deconstruct p (out x))++construct :: Pat a env con -> env -> a+construct PVar (Var x) = x+construct PVar' (Var x) = x+construct (PConst c) _ = c+construct (l `PProd` r) (x, y) = (construct l x, construct r y)+construct (PLeft p) x = Left (construct p x)+construct (PRight p) x = Right (construct p x)+construct (PIn p) x = inn (construct p x)++retrieve :: Direction env a -> env -> a+retrieve DVar (Var x ) = x+retrieve (DLeft d) (env, _) = retrieve d env+retrieve (DRight d) (_, env) = retrieve d env++eval :: Expr env a -> env -> a+eval (EDir d) env = retrieve d env+eval (EConst c) env = c+eval (l `EProd` r) env = (eval l env, eval r env)+eval (ELeft e) env = Left (eval e env)+eval (ERight e) env = Right (eval e env)+eval (EIn e) env = inn (eval e env)++uneval :: Pat a env con -> Expr env b -> b -> con -> Either PatError con+uneval p (EDir d) x con = unevalDir p d x con+uneval p (EConst c) x con = if c == x then return con else throwError PEConstantMismatch+uneval p (EProd l r) (x, y) con = modifyError PEProdL (uneval p l x con) >>= modifyError PEProdR . uneval p r y+uneval p (ELeft e) (Left x) con = modifyError PELeft (uneval p e x con)+uneval p (ELeft _) x con = throwError PELeftMismatch+uneval p (ERight e) (Right x) con = modifyError PERight (uneval p e x con)+uneval p (ERight _) x con = throwError PERightMismatch+uneval p (EIn e) x con = modifyError PEIn (uneval p e (out x) con)++unevalDir :: Pat a env con -> Direction env b -> b -> con -> Either PatError con+unevalDir PVar DVar x (Just y) = if x == y+ then return (Just x)+ else throwError PEIncompatibleUpdates+unevalDir PVar DVar x Nothing = return (Just x)+unevalDir PVar' DVar x (Just y) = throwError PEMultipleUpdates+unevalDir PVar' DVar x Nothing = return (Just x)+unevalDir (PConst c) _ x con = return con+unevalDir (l `PProd` r) (DLeft d) x (conl, conr) = liftM (, conr) (modifyError PEProdL (unevalDir l d x conl))+unevalDir (l `PProd` r) (DRight d) x (conl, conr) = liftM (conl ,) (modifyError PEProdR (unevalDir r d x conr))+unevalDir (PLeft p) d x con = modifyError PELeft (unevalDir p d x con)+unevalDir (PRight p) d x con = modifyError PERight (unevalDir p d x con)+unevalDir (PIn p) d x con = modifyError PEIn (unevalDir p d x con)++fromContainerV :: Pat v env con -> con -> Either PatError env+fromContainerV PVar Nothing = throwError PEValueUnrecoverable+fromContainerV PVar (Just v) = return (Var v)+fromContainerV PVar' Nothing = throwError PEValueUnrecoverable+fromContainerV PVar' (Just v) = return (Var v)+fromContainerV (PConst c) con = return ()+fromContainerV (l `PProd` r) (conl, conr) = liftM2 (,) (modifyError PEProdL (fromContainerV l conl))+ (modifyError PEProdR (fromContainerV r conr))+fromContainerV (PLeft p) con = modifyError PELeft (fromContainerV p con)+fromContainerV (PRight p) con = modifyError PERight (fromContainerV p con)+fromContainerV (PIn p) con = modifyError PEIn (fromContainerV p con)++fromContainerS :: Pat s env con -> env -> con -> env+fromContainerS PVar (Var s) Nothing = (Var s )+fromContainerS PVar (Var s) (Just s') = (Var s')+fromContainerS PVar' (Var s) Nothing = (Var s )+fromContainerS PVar' (Var s) (Just s') = (Var s')+fromContainerS (PConst c) _ _ = ()+fromContainerS (l `PProd` r) (envl, envr) (conl, conr) = (fromContainerS l envl conl, fromContainerS r envr conr)+fromContainerS (PLeft p) env con = fromContainerS p env con+fromContainerS (PRight p) env con = fromContainerS p env con+fromContainerS (PIn p) env con = fromContainerS p env con++emptyContainer :: Pat v env con -> con+emptyContainer PVar = Nothing+emptyContainer PVar' = Nothing+emptyContainer (PConst c) = ()+emptyContainer (l `PProd` r) = (emptyContainer l, emptyContainer r)+emptyContainer (PLeft p) = emptyContainer p+emptyContainer (PRight p) = emptyContainer p+emptyContainer (PIn p) = emptyContainer p
src/Generics/BiGUL/TH.hs view
@@ -1,5 +1,48 @@-module Generics.BiGUL.TH( normal, normal', normalS, normalV, normalV', normalSV, adaptive, adaptiveS, adaptiveV, adaptiveSV, update, deriveBiGULGeneric, rearrS, rearrV) where+-- | A higher-level syntax for programming in BiGUL, implemented in Template Haskell. +module Generics.BiGUL.TH (+ -- * 'GHC.Generics.Generic' instance derivation+ deriveBiGULGeneric+ -- * Rearrangement+ -- | * BiGUL does not support pattern matching for /n/-tuples where /n/ >= 3.+ -- For convenience (but possibly confusingly),+ -- the programmer __can__ use /n/-tuple patterns with the Template Haskell rearrangement syntax,+ -- but these patterns are translated into ones for right-nested pairs.+ -- For example, a 3-tuple pattern @(x, y, z)@ used in a rearrangement is in fact translated into @(x, (y, z))@.+ --+ -- * In a rearranging lambda-expression, if a pattern variable is used more than once in the body,+ -- the type of the pattern variable will be required to be an instance of 'Eq'.+ --+ -- * If an error message+ --+ -- > ‘C’ is not in the type environment at a reify+ --+ -- is reported where @C@ is a constructor used in a rearrangement,+ -- perhaps you forget to invoke 'deriveBiGULGeneric' on @C@’s datatype.+ , rearrS+ , rearrV+ , update+ -- * 'Case' branch construction+ -- | * In the following branch construction syntax, the meaning of a boolean-valued pattern-matching lambda-expression+ -- is redefined as a __total__ function which computes to 'False' when an input does not match the pattern;+ -- this meaning is different from that of a general pattern-matching lambda-expression, which fails to compute+ -- when the pattern is not matched. For example, in general the lambda-expression+ --+ -- > \(s:ss) (v:vs) -> s == v+ --+ -- will fail to compute if one of its inputs is an empty list; when used in branch construction, however,+ -- the lambda-expression will compute to 'False' upon encountering an empty list.+ --+ -- * An argument whose type is an instance of 'ExpOrPat' (a typeclass not exported) can be either+ -- a quoted expression (of type 'Language.Haskell.TH.Q' 'Language.Haskell.TH.Exp'),+ -- which should describe a unary or binary predicate (boolean-valued function), or a quoted pattern+ -- (of type 'Language.Haskell.TH.Q' 'Language.Haskell.TH.Pat'), which is translated into+ -- a unary predicate that computes to 'True' if the pattern is matched, or 'False' otherwise.+ , normal+ , normalSV+ , adaptive+ , adaptiveSV) where+ import Data.Data import Data.Maybe import Data.List as List@@ -8,16 +51,19 @@ import Language.Haskell.TH as TH import qualified Language.Haskell.TH.Syntax as THS import Language.Haskell.TH.Quote-import Generics.BiGUL.AST+import Language.Haskell.TH.Extras (nameOfCon, namesBoundInPat)+import Generics.BiGUL import Control.Monad -data ConTag = L | R- deriving (Show, Data, Typeable)+astNamespace :: String+astNamespace = "Generics.BiGUL" +data ConTag = L | R+ deriving (Show, Data, Typeable) data PatTag = RTag -- ^ view pattern- | STag -- ^ source Pattern+ | STag -- ^ source pattern | ETag -- ^ expression instance Show PatTag where@@ -25,29 +71,33 @@ show _ = "P" contag :: a -> a -> ConTag -> a-contag a1 _ L = a1-contag _ a2 R = a2---class ConTagSeq a where- toConTags :: a -> Name -> [ConTag]-+contag x _ L = x+contag _ y R = y -type TypeConstructor = String+type Namespace = String+type TypeConstructor = String type ValueConstructor = String-type ErrorMessage = String-+type ErrorMessage = String lookupName :: (String -> Q (Maybe Name)) -> ErrorMessage -> String -> Q Name lookupName f errMsg name = f name >>= maybe (fail errMsg) return --- ["Generic", "K1", "U1", ":+:", ":*:", "Rep"]-lookupNames :: [TypeConstructor] -> [ValueConstructor] -> ErrorMessage -> Q ([Name], [Name])-lookupNames typeCList valueCList errMsg = liftM2 (,) (mapM (lookupName lookupTypeName errMsg) typeCList)- (mapM (lookupName lookupValueName errMsg) valueCList)+lookupNames :: Namespace -> [TypeConstructor] -> [ValueConstructor] -> Q ([Name], [Name])+lookupNames namespace typeCList valueCList =+ let qualifiedName c = namespace ++ "." ++ c+ errorMessage c = "‘" ++ c ++ "’ is not in scope (perhaps you forget to import " ++ namespace ++ ")"+ in liftM2 (,) (mapM (\c -> lookupName lookupTypeName (errorMessage c) (qualifiedName c)) typeCList )+ (mapM (\c -> lookupName lookupValueName (errorMessage c) (qualifiedName c)) valueCList) --- Find the Type Dec by Name--- Construct an InstanceDec.+-- | Generate a 'GHC.Generics.Generic' instance for a named datatype+-- so that its constructors can be used in rearranging lambda-expressions.+-- Invoke this function on a datatype @T@ by putting+--+-- > deriveBiGULGeneric ''T+--+-- at the top level of a source file (say, after the definition of @T@).+-- Only simple datatypes and newtypes are supported (no GADTs, for example);+-- type parameters and named fields (record syntax) are supported. deriveBiGULGeneric :: Name -> Q [InstanceDec] deriveBiGULGeneric name = do (name, typeVars, constructors) <-@@ -55,111 +105,105 @@ info <- reify name case info of (TyConI (DataD [] name typeVars constructors _)) -> return (name, typeVars, constructors)- _ -> fail ( "cannot find " ++ nameBase name ++ ", or not a (supported) datatype.")- ([nGeneric, nRep, nK1, nR, nU1, nSum, nProd, nV1, nS1, nSelector, nDataType], [vFrom, vTo, vK1, vL1, vR1, vU1, vProd, vSelName, vDataTypeName, vModuleName, vM1]) <-- lookupNames [ "GHC.Generics." ++ s | s <- ["Generic", "Rep", "K1", "R", "U1", ":+:", ":*:", "V1", "S1", "Selector", "Datatype"] ]- [ "GHC.Generics." ++ s | s <- ["from", "to", "K1", "L1", "R1", "U1", ":*:", "selName", "datatypeName", "moduleName", "M1"] ]- "cannot find type/value constructors from GHC.Generics."+ (TyConI (NewtypeD [] name typeVars constructor _)) -> return (name, typeVars, [constructor])+ _ -> fail ("‘" ++ nameBase name ++ "’ is not in scope or not a (supported) datatype")+ ([nGeneric, nRep, nK1, nR, nU1, nSum, nProd, nV1, nS1, nSelector, nDataType],+ [vFrom, vTo, vK1, vL1, vR1, vU1, vProd, vSelName, vDataTypeName, vModuleName, vM1]) <-+ lookupNames "GHC.Generics"+ ["Generic", "Rep", "K1", "R", "U1", ":+:", ":*:", "V1", "S1", "Selector", "Datatype"]+ ["from", "to", "K1", "L1", "R1", "U1", ":*:", "selName", "datatypeName", "moduleName", "M1"] env <- consToEnv constructors- let selectorsNameList = generateSelectorNames constructors+ selectorsNameList <- generateSelectorNames constructors let selectorDataDMaybeList = generateSelectorDataD selectorsNameList- let selectorDataTypeMaybeList = map (generateSelectorDataType nDataType vDataTypeName vModuleName (maybe "" id (nameModule name))) selectorsNameList+ let selectorDataTypeMaybeList =+ map (generateSelectorDataType nDataType vDataTypeName vModuleName (maybe "" id (nameModule name)))+ selectorsNameList let selectorNameAndConList = zip selectorsNameList constructors let selectorInstanceDecList = map (generateSelectorInstanceDec nSelector vSelName) selectorNameAndConList let fromClauses = map (constructFuncFromClause (vK1, vU1, vL1, vR1, vProd, vM1)) env let toClauses = map (constructFuncToClause (vK1, vU1, vL1, vR1, vProd, vM1)) env- --conTagsClause <- toconTagsClause env- return $ listMaybe2Just selectorDataDMaybeList ++- listMaybe2Just (concat selectorDataTypeMaybeList) ++- listMaybe2Just (concat selectorInstanceDecList) +++ return $ catMaybes selectorDataDMaybeList +++ catMaybes (concat selectorDataTypeMaybeList) +++ catMaybes (concat selectorInstanceDecList) ++ [InstanceD []- (AppT (ConT nGeneric) (generateTypeVarsType name typeVars))- [TySynInstD nRep- (TySynEqn- [generateTypeVarsType name typeVars]- (constructorsToSum (nSum, nV1) (map (constructorToProduct (nK1, nR, nU1, nProd, nS1)) selectorNameAndConList))),- FunD vFrom fromClauses,- FunD vTo toClauses ]+ (AppT (ConT nGeneric) (generateTypeVarsType name typeVars))+ [TySynInstD nRep+ (TySynEqn+ [generateTypeVarsType name typeVars]+ (constructorsToSum (nSum, nV1)+ (map (constructorToProduct (nK1, nR, nU1, nProd, nS1)) selectorNameAndConList))),+ FunD vFrom fromClauses,+ FunD vTo toClauses] ] -listMaybe2Just :: [Maybe a] -> [a]-listMaybe2Just xs = foldr (\a b -> case a of {Just v -> v:b; Nothing -> b}) [] xs---toconTagsClause :: [(Bool, Name, [ConTag], [Name])] -> Q Clause-toconTagsClause env = do- (_, [vEq, vError]) <- lookupNames [] ["==", "error"] "cannot find functions for eq or error."- conTagsVarName <- newName "name"- expEnv <- mapM (\(b, n, conTags, _) -> liftM2 (,) (dataToExpQ (const Nothing) n) (dataToExpQ (const Nothing) conTags)) env- let conTagsClauseBody = (foldr (\(nExp, lrsExp) e -> CondE ((VarE vEq `AppE` nExp) `AppE` VarE conTagsVarName) lrsExp e)- (VarE vError `AppE` LitE (StringL "cannot find name."))- expEnv)- return $ Clause [WildP, VarP conTagsVarName] (NormalB conTagsClauseBody) []--- constructorsToSum :: (Name, Name) -> [Type] -> Type-constructorsToSum (sum, v1) [] = ConT v1 -- empty+constructorsToSum (sum, v1) [] = ConT v1 constructorsToSum (sum, v1) tps = foldr1 (\t1 t2 -> (ConT sum `AppT` t1) `AppT` t2) tps - constructorToProduct :: (Name, Name, Name, Name, Name) -> ([Maybe Name], Con) -> Type constructorToProduct (k1, r, u1, prod, s1) (_, NormalC _ [] ) = ConT u1-constructorToProduct (k1, r, u1, prod, s1) (_, NormalC _ sts) = foldr1 (\t1 t2 -> (ConT prod `AppT` t1 ) `AppT` t2) $ map (AppT (ConT k1 `AppT` ConT r) . snd) sts-constructorToProduct (k1, r, u1, prod, s1) (names, RecC _ sts) = foldr1 (\t1 t2 -> (ConT prod `AppT` t1 ) `AppT` t2) $ map (\(Just n, st) -> AppT (ConT s1 `AppT` ConT n) ((ConT k1 `AppT` ConT r) `AppT` third st)) (zip names sts)-constructorToProduct _ _ = error "not supported Con"--third :: (a, b, c) -> c-third (_, _, z) = z+constructorToProduct (k1, r, u1, prod, s1) (_, NormalC _ sts) =+ foldr1 (\t1 t2 -> (ConT prod `AppT` t1 ) `AppT` t2) (map (AppT (ConT k1 `AppT` ConT r) . snd) sts)+constructorToProduct (k1, r, u1, prod, s1) (names, RecC _ sts) =+ foldr1 (\t1 t2 -> (ConT prod `AppT` t1 ) `AppT` t2)+ (map (\(Just n, (_,_,t)) -> AppT (ConT s1 `AppT` ConT n) ((ConT k1 `AppT` ConT r) `AppT` t)) (zip names sts))+constructorToProduct _ (_, c) =+ error ("Constructor ‘" ++ nameBase (nameOfCon c) ++ "’ is of an unsupported kind") -- Bool indicates: if Normal then False else RecC True constructorToPatAndBody :: Con -> Q (Bool, Name, [Name])-constructorToPatAndBody (NormalC name sts) = liftM (False, name,) $ replicateM (length sts) (newName "var")-constructorToPatAndBody (RecC name sts) = liftM (True, name,) $ replicateM (length sts) (newName "var")-constructorToPatAndBody _ = fail "not supported Cons"-+constructorToPatAndBody (NormalC name sts) = liftM (False, name,) (replicateM (length sts) (newName "var"))+constructorToPatAndBody (RecC name sts) = liftM (True , name,) (replicateM (length sts) (newName "var"))+constructorToPatAndBody c =+ fail ("Constructor ‘" ++ nameBase (nameOfCon c) ++ "’ is of an unsupported kind") zipWithLRs :: [(Bool, Name, [Name])] -> [(Bool, Name, [ConTag], [Name])] zipWithLRs nns = zipWith (\(b, n, ns) lrs -> (b, n, lrs, ns)) nns (constructLRs (length nns)) consToEnv :: [Con] -> Q [(Bool, Name, [ConTag], [Name])]-consToEnv cons = liftM zipWithLRs $ mapM constructorToPatAndBody cons+consToEnv cons = liftM zipWithLRs (mapM constructorToPatAndBody cons) constructFuncFromClause :: (Name, Name, Name, Name, Name, Name) -> (Bool, Name, [ConTag], [Name]) -> Clause-constructFuncFromClause (vK1, vU1, vL1, vR1, vProd, vM1) (b, n, lrs, names) = Clause [ConP n (map VarP names)] (NormalB (wrapLRs lrs (deriveGeneric names))) []+constructFuncFromClause (vK1, vU1, vL1, vR1, vProd, vM1) (b, n, lrs, names) =+ Clause [ConP n (map VarP names)] (NormalB (wrapLRs lrs (deriveGeneric names))) [] where wrapLRs :: [ConTag] -> Exp -> Exp wrapLRs lrs exp = foldr (\lr e -> ConE (contag vL1 vR1 lr) `AppE` e) exp lrs deriveGeneric :: [Name] -> Exp deriveGeneric [] = ConE vU1- deriveGeneric names = foldr1 (\e1 e2 -> (ConE vProd `AppE` e1) `AppE` e2) $ map (\name -> if b then ConE vM1 `AppE` (ConE vK1 `AppE` VarE name) else ConE vK1 `AppE` VarE name) names-+ deriveGeneric names = foldr1 (\e1 e2 -> (ConE vProd `AppE` e1) `AppE` e2)+ (map (\name -> if b then ConE vM1 `AppE` (ConE vK1 `AppE` VarE name)+ else ConE vK1 `AppE` VarE name) names) constructFuncToClause :: (Name, Name, Name, Name, Name, Name) -> (Bool, Name, [ConTag], [Name]) -> Clause-constructFuncToClause (vK1, vU1, vL1, vR1, vProd, vM1) (b, n, lrs, names) = Clause [wrapLRs lrs (deriveGeneric names)] (NormalB (foldl (\e1 name -> e1 `AppE` (VarE name)) (ConE n) names) ) []+constructFuncToClause (vK1, vU1, vL1, vR1, vProd, vM1) (b, n, lrs, names) =+ Clause [wrapLRs lrs (deriveGeneric names)] (NormalB (foldl (\e1 name -> e1 `AppE` (VarE name)) (ConE n) names) ) [] where wrapLRs :: [ConTag] -> TH.Pat -> TH.Pat wrapLRs lrs pat = foldr (\lr p -> ConP (contag vL1 vR1 lr) [p]) pat lrs deriveGeneric :: [Name] -> TH.Pat deriveGeneric [] = ConP vU1 []- deriveGeneric names = foldr1 (\p1 p2 -> ConP vProd [p1, p2]) $ map (\name -> if b then (ConP vM1 ((:[]) (ConP vK1 ((:[]) (VarP name))))) else (ConP vK1 ((:[]) (VarP name)))) names+ deriveGeneric names = foldr1 (\p1 p2 -> ConP vProd [p1, p2])+ (map (\name -> if b then (ConP vM1 ((:[]) (ConP vK1 ((:[]) (VarP name)))))+ else (ConP vK1 ((:[]) (VarP name)))) names) -- construct selector names from constructors-generateSelectorNames :: [Con] -> [[Maybe Name]]-generateSelectorNames = map (\con ->+generateSelectorNames :: [Con] -> Q [[Maybe Name]]+generateSelectorNames = mapM (\con -> case con of {- RecC _ sts -> map (\(n, _, _) -> Just (mkName ( "Selector_" ++ nameBase n))) sts;- _ -> []+ RecC _ sts -> mapM (\(n, _, _) -> newName ("Selector_" ++ nameBase n) >>= return . Just) sts;+ _ -> return [] }) generateSelectorDataD :: [[Maybe Name]] -> [Maybe Dec]-generateSelectorDataD names = map (\name -> case name of {Just n -> Just $ DataD [] n [] [] []; Nothing -> Nothing}) (concat names)+generateSelectorDataD names = map (fmap (\n -> DataD [] n [] [] [])) (concat names) -- Selector DataType Generation generateSelectorDataType :: Name -> Name -> Name -> String -> [Maybe Name] -> [Maybe Dec]-generateSelectorDataType nDataType vDataTypeName vModuleName moduleName = map (generateSelectorDataType' nDataType vDataTypeName vModuleName moduleName)+generateSelectorDataType nDataType vDataTypeName vModuleName moduleName =+ map (generateSelectorDataType' nDataType vDataTypeName vModuleName moduleName) generateSelectorDataType' :: Name -> Name -> Name -> String -> Maybe Name -> Maybe Dec generateSelectorDataType' nDataType vDataTypeName vModuleName moduleName (Just selectorName) =@@ -182,7 +226,6 @@ [FunD vSelName ([Clause [WildP] (NormalB (LitE (StringL (nameBase name)))) []])] generateSelectorInstanceDec' _ _ _ = Nothing - -- generate type representation of polymorhpic type -- e.g. VBook a b is represented as: AppT (ConT name) (ConT name_a `AppT` ConT name_b) generateTypeVarsType :: Name -> [TyVarBndr] -> Type@@ -190,10 +233,9 @@ generateTypeVarsType n tvars = foldl (\a b -> AppT a b) (ConT n) $ map (\tvar -> case tvar of { PlainTV name -> VarT name;- KindedTV name kind -> VarT name-- error "kind type variables are not supported yet."+ KindedTV name kind -> VarT name -- error "kind type variables are not supported yet." }) tvars - constructLRs :: Int -> [[ConTag]] constructLRs 0 = [] constructLRs 1 = [[]]@@ -205,8 +247,12 @@ datatypeName <- case info of DataConI _ _ n _ -> return n- _ -> fail $ nameBase conName ++ " is not a data constructor"- TyConI (DataD _ _ _ cons _) <- reify datatypeName+ _ -> fail $ "‘" ++ nameBase conName ++ "’ is not a data constructor"+ typeInfo <- reify datatypeName+ let cons = case typeInfo of+ TyConI (DataD _ _ _ cons _) -> cons+ TyConI (NewtypeD _ _ _ con _) -> [con]+ _ -> [] return $ constructLRs (length cons) !! fromJust (List.findIndex (== conName) (map (\con -> case con of { NormalC n _ -> n; RecC n _ -> n}) cons)) @@ -216,8 +262,12 @@ datatypeName <- case info of DataConI _ _ n _ -> return n- _ -> fail $ nameBase conName ++ " is not a data constructor"- TyConI (DataD _ _ _ cons _) <- reify datatypeName+ _ -> fail $ "‘" ++ nameBase conName ++ "’ is not a data constructor"+ typeInfo <- reify datatypeName+ let cons = case typeInfo of+ TyConI (DataD _ _ _ cons _) -> cons+ TyConI (NewtypeD _ _ _ con _) -> [con]+ _ -> [] return $ (\(RecC _ fs) -> length fs) (fromJust (List.find (\(RecC n _) -> n == conName) cons)) lookupRecordField :: Name -> Name -> Q Int@@ -226,36 +276,34 @@ datatypeName <- case info of DataConI _ _ n _ -> return n- _ -> fail $ nameBase conName ++ " is not a data constructor"- TyConI (DataD _ _ _ cons _) <- reify datatypeName+ _ -> fail $ "‘" ++ nameBase conName ++ "’ is not a data constructor"+ typeInfo <- reify datatypeName+ let cons = case typeInfo of+ TyConI (DataD _ _ _ cons _) -> cons+ TyConI (NewtypeD _ _ _ con _) -> [con]+ _ -> [] case (List.findIndex (\(n,_,_) -> n == fieldName) ((\(RecC _ fs) -> fs) $ fromJust (List.find (\(RecC n _) -> n == conName) cons))) of Just res -> return res- Nothing -> fail $ nameBase fieldName ++ " is not a field in " ++ nameBase conName-+ Nothing -> fail $ "‘" ++ nameBase fieldName ++ "’ is not a field in ‘" ++ nameBase conName ++ "’" mkConstrutorFromLRs :: [ConTag] -> PatTag -> Q (Exp -> Exp)-mkConstrutorFromLRs lrs patTag = do (_, [gin, gleft, gright]) <- lookupNames [] [ "Generics.BiGUL.AST." ++ show patTag ++ s | s <- ["In", "Left", "Right"] ] "cannot find data constructors *what* from Generic.BiGUL.AST"- return $ foldl (.) (AppE (ConE gin)) (map (AppE . ConE . contag gleft gright) lrs)---astNameSpace :: String-astNameSpace = "Generics.BiGUL.AST."+mkConstrutorFromLRs lrs patTag = do+ (_, [gin, gleft, gright]) <- lookupNames astNamespace [] (map (show patTag ++) ["In", "Left", "Right"])+ return (foldl (.) (AppE (ConE gin)) (map (AppE . ConE . contag gleft gright) lrs)) --- | mkPat :: TH.Pat -> PatTag -> [Name] -> Q TH.Exp mkPat (LitP c) patTag _ = do- (_, [gconst]) <- lookupNames [] [astNameSpace ++ show patTag ++ "Const"] (notFoundMsg $ show patTag ++ "Const")+ (_, [gconst]) <- lookupNames astNamespace [] [show patTag ++ "Const"] return $ ConE gconst `AppE` LitE c - -- user defined datatypes && unit pattern mkPat (ConP name ps) patTag dupnames = do ConP name' [] <- [p| () |] if name == name' && ps == [] then do unitt <- [| () |]- (_, [gconst]) <- lookupNames [] [astNameSpace ++ show patTag ++ s | s <- ["Const"]] (notFoundMsg $ show patTag ++ "Const")+ (_, [gconst]) <- lookupNames astNamespace [] [show patTag ++ "Const"] return $ ConE gconst `AppE` unitt else do lrs <- lookupLRs name@@ -265,8 +313,6 @@ _ -> mkPat (TupP ps) patTag dupnames return $ conInEither pes -- mkPat (RecP name ps) patTag dupnames = do -- reduce the case for a record constructor to the case for an ordinary constructor len <- lookupRecordLength name -- number of constructor arguments@@ -280,14 +326,13 @@ | otherwise = helper (i+1) n pairs (acc++[findInPair pairs i]) -- let ips = zip indexs nps in [ maybe WildP id (List.lookup i ips) | i <- [0..len-1] ] - mkPat (ListP []) patTag dupnames = do emptyp <- [p| [] |] mkPat emptyp patTag dupnames mkPat (ListP (p:xs)) patTag dupnames = do hexp <- mkPat p patTag dupnames rexp <- mkPat (ListP xs) patTag dupnames- (_, [gin,gright,gprod]) <- lookupNames [] [astNameSpace ++ show patTag ++ s | s <- ["In","Right","Prod"]] (notFoundMsg $ (concatWith " ". map (withPatTag patTag)) ["In","Right","Prod"])+ (_, [gin,gright,gprod]) <- lookupNames astNamespace [] (map (show patTag ++) ["In", "Right", "Prod"]) return $ ConE gin `AppE` (ConE gright `AppE` (ConE gprod `AppE` hexp `AppE` rexp)) mkPat (InfixP pl name pr) patTag dupnames = do@@ -295,60 +340,41 @@ if name == name' then do lpat <- mkPat pl patTag dupnames rpat <- mkPat pr patTag dupnames- (_, [gin,gright,gprod]) <- lookupNames [] [astNameSpace ++ show patTag ++ s | s <- ["In","Right","Prod"]] (notFoundMsg $ (concatWith " ". map (withPatTag patTag)) ["In","Right","Prod"])+ (_, [gin,gright,gprod]) <- lookupNames astNamespace [] (map (show patTag ++) ["In", "Right", "Prod"]) return $ ConE gin `AppE` (ConE gright `AppE` (ConE gprod `AppE` lpat `AppE` rpat))- else fail $ "constructors mismatch: " ++ nameBase name ++ " and " ++ nameBase name'-+ else fail $ "Infix use of ‘" ++ nameBase name ++ "’ is not supported" mkPat (TupP [p]) patTag dupnames = mkPat p patTag dupnames+ mkPat (TupP (p:ps)) patTag dupnames = do lexp <- mkPat p patTag dupnames rexp <- mkPat (TupP ps) patTag dupnames- (_, [gprod]) <- lookupNames [] [astNameSpace ++ show patTag ++ s | s <- ["Prod"]] (notFoundMsg "Prod")+ (_, [gprod]) <- lookupNames astNamespace [] [show patTag ++ "Prod"] return ((ConE gprod `AppE` lexp) `AppE` rexp) -+mkPat (WildP) RTag _ = fail $ "Wildcard (‘_’) is forbidden in a view-rearranging pattern" -mkPat (WildP) RTag _ = fail $ "Wildcard(_) connot be used in lambda pattern expression." mkPat (WildP) STag _ = do- (_, [pvar']) <- lookupNames [] [astNameSpace ++ "PVar'"] (notFoundMsg "PVar'")+ (_, [pvar']) <- lookupNames astNamespace [] ["PVar'"] return $ ConE pvar' - mkPat (VarP name) _ dupnames = do- (_, [pvar,pvar']) <- lookupNames [] [ astNameSpace ++ s | s <- ["PVar", "PVar'"] ] (notFoundMsg "PVar,PVar'")+ (_, [pvar,pvar']) <- lookupNames astNamespace [] ["PVar", "PVar'"] return $ if name `elem` dupnames then ConE pvar else ConE pvar' ---mkPat _ patTag _ = fail $ "Pattern not handled yet."-------+mkPat _ patTag _ = fail "Unsupported pattern in a rearranging lambda-expression" -- | translate all (VarE name) to directions using env rearrangeExp :: Exp -> Map String Exp -> Q Exp-rearrangeExp (VarE name) env =+rearrangeExp (VarE name) env = case Map.lookup (nameBase name) env of Just val -> return val- Nothing -> fail $ "cannot find name " ++ nameBase name ++ " in env."+ Nothing -> fail $ "Panic: Unbound variable ‘" ++ nameBase name ++ "’" rearrangeExp (AppE e1 e2) env = liftM2 AppE (rearrangeExp e1 env) (rearrangeExp e2 env)-rearrangeExp (ConE name) env = return $ ConE name-rearrangeExp (LitE c) env = return $ LitE c-rearrangeExp _ env = fail $ "Invalid representation of bigul program in TemplateHaskell ast"--------+rearrangeExp (ConE name) env = return $ ConE name+rearrangeExp (LitE c) env = return $ LitE c+rearrangeExp _ env = fail "Unsupported expression in a rearranging lambda-expression" mkEnvForRearr :: TH.Pat -> Q (Map String Exp) mkEnvForRearr (LitP c) = return Map.empty@@ -369,21 +395,21 @@ mkEnvForRearr (ListP []) = return Map.empty mkEnvForRearr (ListP (pl:pr)) = do- (_, [dleft,dright]) <- lookupNames [] [ astNameSpace ++ s | s <- ["DLeft", "DRight"] ] (notFoundMsg "DLeft, DRight")+ (_, [dleft,dright]) <- lookupNames astNamespace [] ["DLeft", "DRight"] lenv <- mkEnvForRearr pl renv <- mkEnvForRearr (ListP pr) return $ Map.map (ConE dleft `AppE`) lenv `Map.union` Map.map (ConE dright `AppE`) renv mkEnvForRearr (InfixP pl name pr) = do- (_, [dleft,dright]) <- lookupNames [] [ astNameSpace ++ s | s <- ["DLeft", "DRight"] ] (notFoundMsg "DLeft, DRight")+ (_, [dleft,dright]) <- lookupNames astNamespace [] ["DLeft", "DRight"] lenv <- mkEnvForRearr pl renv <- mkEnvForRearr pr- return $ Map.map (ConE dleft `AppE`) lenv `Map.union`- Map.map (ConE dright `AppE`) renv+ return $ Map.map (ConE dleft `AppE`) lenv `Map.union`+ Map.map (ConE dright `AppE`) renv mkEnvForRearr (TupP ps) = do- (_, [dleft,dright]) <- lookupNames [] [ astNameSpace ++ s | s <- ["DLeft", "DRight"] ] (notFoundMsg "DLeft, DRight")+ (_, [dleft,dright]) <- lookupNames astNamespace [] ["DLeft", "DRight"] subenvs <- mapM mkEnvForRearr ps let envs = zipWith (Map.map . foldr (.) id . map (AppE . ConE . contag dleft dright)) (constructLRs (length ps)) subenvs@@ -392,13 +418,10 @@ mkEnvForRearr WildP = return Map.empty mkEnvForRearr (VarP name) = do- (_, [dvar]) <- lookupNames [] [ astNameSpace ++ s | s <- ["DVar"] ] (notFoundMsg "DVar")+ (_, [dvar]) <- lookupNames astNamespace [] ["DVar"] return $ Map.singleton (nameBase name) (ConE dvar) -mkEnvForRearr _ = fail $ "Pattern not handled yet."---+mkEnvForRearr _ = fail "Unsupported pattern in a rearranging lambda-expression" splitDataAndCon:: TH.Exp -> Q (TH.Exp -> TH.Exp ,[TH.Exp])@@ -414,14 +437,13 @@ d <- mkBodyExpForRearr e2 return (c,ds++[d]) -splitDataAndCon _ = fail $ "Invalid data constructor in lambda body expression"-+splitDataAndCon _ = fail "Invalid data constructor in a rearranging lambda-expression" mkBodyExpForRearr :: TH.Exp -> Q TH.Exp mkBodyExpForRearr (LitE c) = do- (_, [econst]) <- lookupNames [] [astNameSpace ++ "EConst"] (notFoundMsg "EConst")+ (_, [econst]) <- lookupNames astNamespace [] ["EConst"] return $ ConE econst `AppE` (LitE c) mkBodyExpForRearr (VarE name) = return $ VarE name@@ -429,15 +451,14 @@ mkBodyExpForRearr (AppE e1 e2) = do -- must be constructor applied to arguments (rearrangement expression does not allow general functions) -- surface syntax is curried constructor applied to arguments in order; should translate that to uncurried constructor applied to a tuple of arguments- (_, [eprod]) <- lookupNames [] [astNameSpace ++ "EProd"] (notFoundMsg "EProd")+ (_, [eprod]) <- lookupNames astNamespace [] ["EProd"] (con, ds) <- splitDataAndCon (AppE e1 e2) return $ con (foldr1 (\d1 d2 -> ConE eprod `AppE` d1 `AppE` d2) ds) - mkBodyExpForRearr (ConE name) = do -- must be constructor without argument (ConE name') <- [| () |]- (_, [econst]) <- lookupNames [] [astNameSpace ++ s | s <- ["EConst"] ] (notFoundMsg "EConst")+ (_, [econst]) <- lookupNames astNamespace [] ["EConst"] if name == name' then return $ ConE econst `AppE` (ConE name) else mkBodyExpForRearr (AppE (ConE name) (ConE name'))@@ -445,7 +466,7 @@ mkBodyExpForRearr (RecConE name es) = do -- reduce to the case for ordinary constructors (ConE name') <- [| () |]- (_, [econst,eprod]) <- lookupNames [] [astNameSpace ++ s | s <- ["EConst","EProd"]] (notFoundMsg "EConst and EProd")+ (_, [econst,eprod]) <- lookupNames astNamespace [] ["EConst", "EProd"] len <- lookupRecordLength name indexs <- mapM (\(n,_) -> lookupRecordField name n) es let nes = map snd es@@ -462,37 +483,33 @@ if name == name' then do le <- mkBodyExpForRearr e1 re <- mkBodyExpForRearr e2- (_, [ein,eright,eprod]) <- lookupNames [] [astNameSpace ++ s | s <- ["EIn","ERight","EProd"]] (notFoundMsg "EIn, ERight, EProd")+ (_, [ein,eright,eprod]) <- lookupNames astNamespace [] ["EIn", "ERight", "EProd"] return $ ConE ein `AppE` (ConE eright `AppE` (ConE eprod `AppE` le `AppE` re))- else fail $ "only (:) infix operator is allowed in lambda body expression"+ else fail $ "Infix use of ‘" ++ nameBase name ++ "’ is not supported" mkBodyExpForRearr (ListE []) = do unitt <- [| () |]- (_, [ein,eleft,econst]) <- lookupNames [] [astNameSpace ++ s | s <- ["EIn","ELeft","EConst"]] (notFoundMsg "EIn, ELeft, EConst")+ (_, [ein,eleft,econst]) <- lookupNames astNamespace [] ["EIn", "ELeft", "EConst"] return $ ConE ein `AppE` (ConE eleft `AppE` (ConE econst `AppE` unitt)) mkBodyExpForRearr (ListE (e:es)) = do hexp <- mkBodyExpForRearr e rexp <- mkBodyExpForRearr (ListE es)- (_, [ein,eright,eprod]) <- lookupNames [] [astNameSpace ++ s | s <- ["EIn","ERight","EProd"]] (notFoundMsg "EIn, ERight, EProd")+ (_, [ein,eright,eprod]) <- lookupNames astNamespace [] ["EIn", "ERight", "EProd"] return $ ConE ein `AppE` (ConE eright `AppE` (ConE eprod `AppE` hexp `AppE` rexp)) mkBodyExpForRearr (TupE [e]) = mkBodyExpForRearr e mkBodyExpForRearr (TupE (e:es)) = do lexp <- mkBodyExpForRearr e rexp <- mkBodyExpForRearr (TupE es)- (_, [eprod]) <- lookupNames [] [astNameSpace ++ "EProd"] (notFoundMsg "EProd")+ (_, [eprod]) <- lookupNames astNamespace [] ["EProd"] return ((ConE eprod `AppE` lexp) `AppE` rexp)-mkBodyExpForRearr _ = fail $ "Invalid syntax in lambda body expression"----+mkBodyExpForRearr _ = fail "Unsupported expression in a rearranging lambda-expression" rearr' :: PatTag -> TH.Exp -> [Name] -> Q TH.Exp rearr' patTag (LamE [p] e) dupnames = do let suffixRS = case patTag of {RTag -> "V" ; STag -> "S" ; _ -> ""}- (_, [edir,rearrc]) <- lookupNames [] [astNameSpace ++ s | s <- ["EDir","Rearr"++suffixRS] ] (notFoundMsg $ "EDir, Rearr"++suffixRS)+ (_, [edir,rearrc]) <- lookupNames astNamespace [] ["EDir", "Rearr" ++ suffixRS] pat <- mkPat p patTag dupnames exp <- mkBodyExpForRearr e env <- mkEnvForRearr p@@ -508,24 +525,52 @@ getAllVars (InfixE (Just e1) (ConE name) (Just e2)) = getAllVars e1 ++ getAllVars e2 getAllVars (ListE es) = concatMap getAllVars es getAllVars (TupE es) = concatMap getAllVars es-getAllVars _ = fail $ "Invalid exp in getAllVars"---rearrV :: Q TH.Exp -> Q TH.Exp-rearrV qlambexp = do lambexp@(LamE _ e) <- qlambexp- let varnames = getAllVars e- rearr' RTag lambexp (varnames \\ (nub varnames))--rearrS :: Q TH.Exp -> Q TH.Exp-rearrS qlambexp = do lambexp@(LamE _ e) <- qlambexp- let varnames = getAllVars e- rearr' STag lambexp (varnames \\ (nub varnames))----+getAllVars _ = fail "Unsupported expression in a rearranging lambda-expression" +-- | A higher-level syntax for 'Generics.BiGUL.RearrS',+-- allowing its first and second arguments to be specified in terms of a simple lambda-expression.+-- The usual way of using 'rearrS' is+--+-- > $(rearrS [| f |]) b :: BiGUL s v+--+-- where @f :: s -> s'@ is a simple lambda-expression and @b :: BiGUL s' v@ an inner program.+rearrS :: Q TH.Exp -- ^ rearranging lambda-expression+ -> Q TH.Exp+rearrS qlambexp = do+ lambexp <- qlambexp+ case lambexp of+ LamE [_] e ->+ let varnames = getAllVars e+ in rearr' STag lambexp (varnames \\ nub varnames)+ LamE _ _ ->+ fail "A rearranging lambda-expression should have exactly one argument"+ _ ->+ fail "The first argument to rearrS should be a (quoted) lambda-expression" +-- | A higher-level syntax for 'Generics.BiGUL.RearrV',+-- allowing its first and second arguments to be specified in terms of a simple lambda-expression.+-- The usual way of using 'rearrV' is+--+-- > $(rearrV [| f |]) b :: BiGUL s v+--+-- where @f :: v -> v'@ is a simple lambda-expression and @b :: BiGUL s v'@ an inner program.+-- In @f@, wildcard ‘@_@’ is not allowed, and all pattern variables must be used in the body.+-- (This is for ensuring that the view information is fully embedded into the source.)+rearrV :: Q TH.Exp -- ^ rearranging lambda-expression+ -> Q TH.Exp+rearrV qlambexp = do+ lambexp <- qlambexp+ case lambexp of+ LamE [p] e ->+ let varnames = getAllVars e+ unusedVars = namesBoundInPat p \\ varnames+ in if null unusedVars+ then rearr' RTag lambexp (varnames \\ nub varnames)+ else fail $ "Variable(s) unused in the body of a view-rearranging lambda-expression: " +++ concat (intersperse ", " (map nameBase unusedVars))+ LamE _ _ -> fail "A rearranging lambda-expression should have exactly one argument"+ _ ->+ fail "The first argument to rearrV should be a (quoted) lambda-expression" mkExpFromPat :: TH.Pat -> Q TH.Exp mkExpFromPat (LitP c) = return (LitE c)@@ -548,25 +593,25 @@ return (TupE es) mkExpFromPat (VarP name) = return (VarE name) mkExpFromPat WildP = [| () |]-mkExpFromPat _ = fail $ "pattern not handled in mkExpFromPat"+mkExpFromPat _ = fail "Unsupported pattern in a rearranging lambda-expression" mkExpFromPat' :: TH.Pat -> Q TH.Exp-mkExpFromPat' (ConP name ps ) = do (_, [replace]) <- lookupNames [] [astNameSpace ++ "Replace"] (notFoundMsg "Replace")+mkExpFromPat' (ConP name ps ) = do (_, [replace]) <- lookupNames astNamespace [] ["Replace"] ConP name' [] <- [p| () |] if name == name' && ps == [] then return (ConE replace)- else fail $ "rearrSV only supports tuple"+ else fail $ "Panic: rearrSV only supports tuple" mkExpFromPat' (VarP name) = return (VarE name) mkExpFromPat' (TupP ps) = do- (_, [prod]) <- lookupNames [] [ astNameSpace ++ "Prod" ] (notFoundMsg "Prod")+ (_, [prod]) <- lookupNames astNamespace [] ["Prod"] es <- mapM mkExpFromPat' ps return $ foldr1 (\e1 e2 -> ((ConE prod `AppE` e1) `AppE` e2)) es-mkExpFromPat' _ = fail $ "rearrSV only supports tuple"+mkExpFromPat' _ = fail $ "Panic: rearrSV only supports tuple" toProduct :: TH.Exp -> Q TH.Exp toProduct (AppE e1 e2) = do (ConE unitn) <- [| () |]- (_, [econst,ein,eleft,eright]) <- lookupNames [] [ astNameSpace ++ s | s <- ["EConst","EIn","ELeft", "ERight"] ] (notFoundMsg "EConst, EIn, ELeft, ERight")+ (_, [econst,ein,eleft,eright]) <- lookupNames astNamespace [] ["EConst", "EIn", "ELeft", "ERight"] re2 <- toProduct e2 re1 <- toProduct e1 if e1 == (ConE eleft) || e1 == (ConE eright) || e1 == (ConE ein)@@ -574,12 +619,8 @@ else if e1 == (ConE econst) then return (AppE e1 (ConE unitn)) else return (AppE re1 re2)-- toProduct other = return other -- mkProdPatFromSHelper :: TH.Pat -> Q TH.Pat mkProdPatFromSHelper (TupP []) = [p| () |] mkProdPatFromSHelper other = return other@@ -606,18 +647,21 @@ mkProdPatFromSHelper (TupP es) mkProdPatFromS (VarP name) = return (VarP name) mkProdPatFromS WildP = [p| () |]-mkProdPatFromS _ = fail $ "pattern not handled in mkProdPatFromS"+mkProdPatFromS _ = fail "Unsupported pattern in a rearranging lambda-expression" -- | Example: rearrSV [p| x:xs |] [p| x:xs |] [p| (x,xs) |] [d| x = Replace; xs = rec |] -- generates a rearrS from the first pattern and the third pattern -- and a rearrV from the second pattern and the third pattern rearrSV :: Q TH.Pat -> Q TH.Pat -> Q TH.Pat -> Q [TH.Dec] -> Q TH.Exp rearrSV qsp qvp qpp qpd = do- (_, [edir,rearrs,rearrv]) <- lookupNames [] [astNameSpace ++ s | s <- ["EDir","RearrS","RearrV"] ] (notFoundMsg "EDir, RearrS, RearrV")+ (_, [edir,rearrs,rearrv]) <- lookupNames astNamespace [] ["EDir", "RearrS", "RearrV"] sp <- qsp vp <- qvp pp <- qpp pd <- qpd+ prodenv <- mkEnvForUpdate pd+ let namesInPat = sort . map nameBase . namesBoundInPat+ checkVars (namesInPat sp) (namesInPat vp) (namesInPat pp) (Map.keys prodenv) spat <- mkPat sp STag [] vpat <- mkPat vp RTag [] commonexp <- mkExpFromPat pp@@ -628,125 +672,158 @@ venv <- mkEnvForRearr vp vbody <- rearrangeExp commonexp'' (Map.map (ConE edir `AppE`) venv) prodexp <- mkExpFromPat' pp- prodenv <- mkEnvForUpdate pd prodbigul <- rearrangeExp prodexp prodenv return $ ((ConE rearrs `AppE` spat) `AppE` sbody) `AppE` (((ConE rearrv `AppE` vpat) `AppE` vbody) `AppE` prodbigul)-+ where+ checkVars :: [String] -> [String] -> [String] -> [String] -> Q ()+ checkVars svars vvars cvars dvars | svars /= vvars =+ fail "Source and view patterns should have the same variables"+ checkVars svars vvars cvars dvars | svars /= cvars =+ fail "The common pattern should have the same variables as the source/view patterns"+ checkVars svars vvars cvars dvars | svars /= dvars =+ fail "The declaration list should include exactly the variables in the source/view patterns"+ checkVars svars vvars cvars dvars | otherwise = return () -update :: Q TH.Pat -> Q TH.Pat -> Q [TH.Dec] -> Q TH.Exp-update = \pv ps d -> rearrSV ps pv (ps >>= mkProdPatFromS) d+-- | A succinct syntax dealing with the frequently occurring situation where both the source and view+-- are rearranged into products and their components further synchronised by inner updates.+-- For example, the program+--+-- > $(update [p| x:xs |] [p| x:xs |] [d| x = Replace; xs = b |]) :: BiGUL [a] [a]+--+-- matches both the source and view lists with a cons pattern, marking their head and tail as @x@ and @xs@ respectively,+-- and synchronises the heads using @Replace@ (which is the program associated with @x@ in the declaration list)+-- and the tails using some @b :: BiGUL [a] [a]@. In short, the program is equivalent to+--+-- > $(rearrS [| \(x:xs) -> (x, xs) |])$+-- > $(rearrV [| \(x:xs) -> (x, xs) |])$+-- > Replace `Prod` b+--+-- (Admittedly, it is an abuse of syntax to represent a list of named BiGUL programs in terms of a declaration list,+-- but it is the closest thing we can find that works directly with Template Haskell.)+update :: Q TH.Pat -- ^ source pattern+ -> Q TH.Pat -- ^ view pattern+ -> Q [TH.Dec] -- ^ named updates (as a declaration list)+ -> Q TH.Exp+update ps pv d = rearrSV ps pv (ps >>= mkProdPatFromS) d mkEnvForUpdate :: [TH.Dec] -> Q (Map String TH.Exp) mkEnvForUpdate [] = return Map.empty mkEnvForUpdate ((ValD (VarP name) (NormalB e) _ ):ds) = do renv <- mkEnvForUpdate ds return $ Map.singleton (nameBase name) e `Map.union` renv-mkEnvForUpdate (_:ds) = fail $ "Invalid syntax in update bindings\n" ++- "Please use syntax like x1 = e1 x2 = e2... here"--{--update :: Q TH.Pat -> Q [TH.Dec] -> Q TH.Exp-update qp qds = do- (_, [upd]) <- lookupNames [] [astNameSpace ++ "Update"] (notFoundMsg "Update")- p <- qp- ds <- qds- pat <- mkPat p UTag- env <- mkEnvForUpdate ds- rearrangeExp (ConE upd `AppE` pat) env--}----patToFunc :: TH.Pat -> Q TH.Exp-patToFunc p = do- (_, [htrue,hfalse]) <- lookupNames [] ["True","False"] (notFoundMsg "True,False")- name <- newName "x"- case p of- TH.WildP -> return $ LamE [VarP name] (ConE htrue)- _ -> return $ LamE [VarP name] (CaseE (VarE name)- [Match p (NormalB (ConE htrue)) [], Match WildP (NormalB (ConE hfalse)) []])---------notFoundMsg :: String -> String-notFoundMsg s = "cannot find data constructors " ++ s ++ " from Generic.BiGUL.AST"+mkEnvForUpdate (_:ds) = fail "Invalid syntax in update bindings (write ‘x1 = e1; x2 = e2; ...’)" -withPatTag :: PatTag -> String -> String-withPatTag tag con = show tag ++ con+patCond :: TH.Pat -> Q TH.Exp+patCond p = do+ (_, [htrue,hfalse]) <- lookupNames "Prelude" [] ["True", "False"]+ var <- newName "x"+ return $ case p of+ TH.WildP -> LamE [VarP var] (ConE htrue)+ _ -> LamE [VarP var] (CaseE (VarE var)+ [Match p (NormalB (ConE htrue)) [], Match WildP (NormalB (ConE hfalse)) []]) -concatWith :: String -> [String] -> String-concatWith sep [] = ""-concatWith sep (x:xs) = x ++ sep ++ concatWith sep xs+nameAdaptive :: Q TH.Exp+nameAdaptive = lookupNames astNamespace [] ["Adaptive"] >>= \(_, [badaptive]) -> conE badaptive +nameNormal :: Q TH.Exp+nameNormal = lookupNames astNamespace [] ["Normal"] >>= \(_, [bnormal]) -> conE bnormal class ExpOrPat a where- toExp :: a -> TH.ExpQ+ toExp :: a -> Q TH.Exp -instance ExpOrPat (TH.ExpQ) where+instance ExpOrPat (Q TH.Exp) where toExp = id -instance ExpOrPat (TH.PatQ) where- toExp = (>>= patToFunc)---- $(normal [| predicateOnSV |]) b--- ~> (predicateOnSV, Normal b (const True))-normal :: TH.ExpQ -> TH.ExpQ-normal psv = [|\b -> ($psv, $(nameNormal) b (const True))|]----- $(normal' [| predicateOnSV |] [| predictionPredicate |]) b--- ~> (predicateOnSV, Normal b predictionPredicate)-normal' :: ExpOrPat a => TH.ExpQ -> a -> TH.ExpQ-normal' psv pp = [|\b -> ($psv, $(nameNormal) b $(toExp pp)) |]----- $(normalS [| predicateOnS |]) b--- ~> ((\s _ -> predicateOnS s), Normal b predicateOnS)-normalS :: ExpOrPat a => a -> TH.ExpQ-normalS ps = [|\b -> (\s _ -> $(toExp ps) s, $(nameNormal) b $(toExp ps)) |]---- $(normalV [| predicateOnV |]) b--- ~> ((\_ v -> predicateOnV v), Normal b (const True))-normalV :: ExpOrPat a => a -> TH.ExpQ-normalV pv = [|\b -> (\_ v -> $(toExp pv) v, $(nameNormal) b (const True)) |]---- $(normalV' [| predicateOnV |] [| predictionPredicate |]) b--- ~> ((\_ v -> predicateOnV v), Normal b predictionPredicate)-normalV' :: (ExpOrPat a, ExpOrPat b) => a -> b -> TH.ExpQ-normalV' pv pp = [|\b -> (\_ v -> $(toExp pv) v, $(nameNormal) b $(toExp pp)) |]---- $(normalSV [| predicateOnS |] [| predicateOnV |]) b--- ~> ((\s v -> predicateOnS s && predicateOnV v), Normal b predicateOnS)-normalSV :: (ExpOrPat a, ExpOrPat b) => a -> b -> TH.ExpQ-normalSV ps pv = [|\b -> (\s v -> $(toExp ps) s && $(toExp pv) v, $(nameNormal) b $(toExp ps)) |]---- $(adaptive [| predicateOnSV |]) f--- ~> (predicateOnSV, Adaptive f)-adaptive :: TH.ExpQ -> TH.ExpQ-adaptive psv = [| \f -> ($psv, $(nameAdaptive) f) |]---- $(adaptiveS [| predicateOnS |]) f--- ~> ((\s _ -> predicateOnS s), Adaptive f)-adaptiveS :: ExpOrPat a => a -> TH.ExpQ-adaptiveS ps = [| \f -> (\s _ -> $(toExp ps) s, $(nameAdaptive) f) |]---- $(adaptiveV [| predicateOnV |]) f--- ~> ((\_ v -> predicateOnV v), Adaptive f)-adaptiveV :: ExpOrPat a => a -> TH.ExpQ-adaptiveV pv = [| \f -> (\_ v -> $(toExp pv) v, $(nameAdaptive) f) |]+instance ExpOrPat (Q TH.Pat) where+ toExp = (>>= patCond) --- $(adaptiveSV [| predicateOnS |] [| predicateOnV |]) f--- ~> ((\s v -> predicateOnS s && predicateOnV v), Adaptive f)-adaptiveSV :: (ExpOrPat a, ExpOrPat b) => a -> b -> TH.ExpQ-adaptiveSV ps pv = [| \f -> (\s v -> $(toExp ps) s && $(toExp pv) v, $(nameAdaptive) f) |]+patLambdaToPred :: TH.Exp -> Q TH.Exp+patLambdaToPred p =+ case p of+ LamE [pat] body -> do+ (_, [hmaybe, hFalse, hid, hreturn]) <-lookupNames "Prelude" [] ["maybe", "False", "id", "return"]+ [| \x -> $(varE hmaybe) $(conE hFalse) $(varE hid) $(doExp hreturn pat [| x |] body) |]+ LamE [spat, vpat] body -> do+ (_, [hmaybe, hFalse, hid, hreturn]) <-lookupNames "Prelude" [] ["maybe", "False", "id", "return"]+ [| \s v -> $(varE hmaybe) $(conE hFalse) $(varE hid) $(doExp hreturn (TupP [spat, vpat]) [| (s, v) |] body) |]+ _ -> return p+ where+ doExp :: TH.Name -> TH.Pat -> Q TH.Exp -> TH.Exp -> Q TH.Exp+ doExp hreturn p qMatchExp boolExp = do+ matchExp <- qMatchExp+ return (DoE [BindS p (VarE hreturn `AppE` matchExp),+ NoBindS (VarE hreturn `AppE` boolExp)]) +-- | Construct a normal branch, for which a main condition on the source and view and+-- an exit condition on the source should be specified. The usual way of using 'normal' is+--+-- > $(normal [| p |] [| q |]) b :: CaseBranch s v+--+-- where+--+-- * @p :: s -> v -> Bool@,+--+-- * @q :: s -> Bool@, and+--+-- * @b :: BiGUL s v@, which is the branch body.+normal :: ExpOrPat a+ => Q TH.Exp -- ^ main condition (binary predicate on the source and view)+ -> a -- ^ exit condition (unary predicate on the source)+ -> Q TH.Exp+normal mp mq =+ [| \b -> ($(mp >>= patLambdaToPred), $(nameNormal) b $(toExp mq >>= patLambdaToPred)) |] -nameAdaptive :: TH.ExpQ-nameAdaptive = lookupNames [] [astNameSpace ++ "Adaptive"] (notFoundMsg "Adaptive") >>= \(_, [badaptive]) -> conE badaptive+-- | A special case of 'normal' where the main condition is specified as the conjunction of two unary predicates+-- on the source and view respectively. The usual way of using 'normalSV' is+--+-- > $(normalSV [| ps |] [| pv |] [| q |]) b :: CaseBranch s v+--+-- where+--+-- * @ps :: s -> Bool@,+--+-- * @pv :: v -> Bool@,+--+-- * @q :: s -> Bool@, and+--+-- * @b :: BiGUL s v@, which is the branch body.+normalSV :: (ExpOrPat a, ExpOrPat b, ExpOrPat c)+ => a -- ^ main source condition (unary predicate on the source)+ -> b -- ^ main view condition (unary predicate on the view)+ -> c -- ^ exit condition (unary predicate on the source)+ -> Q TH.Exp+normalSV mps mpv mq =+ [|\b -> (\s v -> $(toExp mps) s && $(toExp mpv) v, $(nameNormal) b $(toExp mq >>= patLambdaToPred)) |] -nameNormal :: TH.ExpQ-nameNormal = lookupNames [] [astNameSpace ++ "Normal"] (notFoundMsg "Normal") >>= \(_, [bnormal]) -> conE bnormal+-- | Construct an adaptive branch, for which a main condition on the source and view should be specified.+-- The usual way of using 'adaptive' is+--+-- > $(adaptive [| p |]) f :: CaseBranch s v+--+-- where+--+-- * @p :: s -> v -> Bool@ and+--+-- * @f :: s -> v -> s@, which is the adaptation function.+adaptive :: Q TH.Exp -- ^ main condition (binary predicate on the source and view)+ -> Q TH.Exp+adaptive mp = [| \f -> ($(mp >>= patLambdaToPred), $(nameAdaptive) f) |] +-- | A special case of 'adaptive' where the main condition is specified as the conjunction of two unary predicates+-- on the source and view respectively. The usual way of using 'adaptiveSV' is+--+-- > $(adaptiveSV [| ps |] [| pv |]) f :: CaseBranch s v+--+-- where+--+-- * @ps :: s -> Bool@,+--+-- * @pv :: v -> Bool@, and+--+-- * @f :: s -> v -> s@, which is the adaptation function.+adaptiveSV :: (ExpOrPat a, ExpOrPat b)+ => a -- ^ main source condition (unary predicate on the source)+ -> b -- ^ main view condition (unary predicate on the view)+ -> Q TH.Exp+adaptiveSV ps pv =+ [| \f -> (\s v -> $(toExp ps >>= patLambdaToPred) s && $(toExp pv >>= patLambdaToPred) v, $(nameAdaptive) f) |]