-- -- (c) Susumu Katayama -- -- This file is supposed to be used with Version 0.8.5 of MagicHaskeller. -- For previous versions, try: -- darcs get http://nautilus.cs.miyazaki-u.ac.jp/~skata/ somedirectoryname -- and retrieve an older version via some darcs command. {-# OPTIONS -XTemplateHaskell -XNoMonomorphismRestriction -fglasgow-exts #-} module MagicHaskeller.LibTH(module MagicHaskeller.LibTH, module MagicHaskeller) where import MagicHaskeller import System.Random(mkStdGen) import Control.Monad(liftM2) import Data.List hiding (tail, init) import Data.Char import Data.Maybe import qualified Data.Generics as G import Prelude hiding (tail, init, gcd) -- total variants of prelude functions tail = drop 1 init xs = zipWith const xs (drop 1 xs) -- gcd in the latest library is total, but with older versions gcd 0 0 causes an error. gcd x y = gcd' (abs x) (abs y) where gcd' a 0 = a gcd' a b = gcd' b (a `rem` b) initialize, init075, inittv1 :: IO () initialize = do setPrimitives (list ++ nat ++ natural ++ mb ++ bool ++ $(p [| (hd :: (->) [a] (Maybe a), (+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |])) setDepth 10 -- MagicHaskeller version 0.8 ignores the setDepth value and always memoizes. init075 = do setPG $ mkMemo075 (list ++ nat ++ natural ++ mb ++ bool ++ $(p [| ((+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |] )) setDepth 10 -- The @tv1@ option prevents type variable @a@ in @forall a. E1(a) -> E2(a) -> ... -> En(a) -> a@ from matching n-ary functions where n>=2. -- This can safely be used if @(,)@ and @uncurry@ are in the primitive set, -- because @forall a b c. E1(a->b->c) -> E2(a->b->c) -> ... -> En(a->b->c) -> a -> b -> c@ and @forall a b c. E1((a,b)->c) -> E2((a,b)->c) -> ... -> En((a,b)->c) -> (a,b) -> c@ are isomorphic, and thus the latter can always be used instead of the former. inittv1 = do setPG $ mkPGOpt (options{primopt = Nothing, tv1 = True}) (list ++ nat ++ natural ++ mb ++ bool ++ tuple ++ $(p [| (hd :: (->) [a] (Maybe a), (+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |] )) setDepth 10 tuple = $(p [| ((,) :: a -> b -> (a,b), uncurry :: (a->b->c) -> (->) (a,b) c) |]) tuple' = $(p [| ((,) :: a -> b -> (a,b), flip uncurry :: (->) (a,b) ((a->b->c) -> c)) |]) -- Specialized memoization tables. Choose one for quicker results. mall, mlist, mlist', mnat, mlistnat, mnat_nc, mnatural, mlistnatural :: ProgramGenerator pg => pg mall = mkPG (list ++ nat ++ natural ++ mb ++ bool ++ $(p [| (hd :: (->) [a] (Maybe a), (+) :: Int -> Int -> Int, (+) :: Integer -> Integer -> Integer) |])) mlist = mkPG list mlist' = mkPG list' mnat = mkPG (nat ++ $(p [| (+) :: Int -> Int -> Int |])) mnatural = mkPG (natural ++ $(p [| (+) :: Integer -> Integer -> Integer |])) mlistnat = mkPG (list ++ nat ++ $(p [| (+) :: Int -> Int -> Int |])) mlistnatural = mkPG (list ++ natural ++ $(p [| (+) :: Integer -> Integer -> Integer |])) mnat_nc = mkMemo (nat ++ $(p [| (+) :: Int -> Int -> Int |])) hd :: [a] -> Maybe a hd [] = Nothing hd (x:_) = Just x -- Prefixed (->) means that the parameter can be matched as an assumption when 'constrL' option is True. Also, this info is used when 'guess' option is True. For example of maybe :: a -> (b->a) -> (->) (Maybe b) a, -- Gamma |- A Gamma,B |- A -- ---------------------------maybe -- Gamma, Maybe B |- A -- rather than -- Gamma |- A Gamma,B |- A Gamma |- Maybe B -- -----------------------------------------------maybe -- Gamma |- A -- This is just for the efficiency reason, and one can use the infixed form, i.e., maybe :: a -> (b->a) -> Maybe b -> a, if efficiency does not matter. In fact, this info is ignored if both 'guess' and 'constrL' options are False. mb, mb', nat, natural, nat', nat'woPred, natural', list'', list', list, bool, boolean, eq, intinst, list1, list1', list2, list3, list3', nats, tuple, tuple', rich, rich', debug :: [Primitive] mb = $(p [| ( Nothing :: Maybe a, Just :: a -> Maybe a, maybe :: a -> (b->a) -> (->) (Maybe b) a ) |] ) mb' = $(p [| ( Nothing :: Maybe a, Just :: a -> Maybe a, flip . maybe :: a -> (->) (Maybe b) ((b->a) -> a) ) |] ) nat = $(p [| (0 :: Int, succ :: Int->Int, nat_para :: (->) Int (a -> (Int -> a -> a) -> a)) |] ) natural = $(p [| (0 :: Integer, succ :: Integer->Integer, nat_para :: (->) Integer (a -> (Integer -> a -> a) -> a)) |] ) nat' = $(p [| (0 :: Int, succ :: Int->Int, nat_cata :: (->) Int (a -> (a -> a) -> a), pred :: Int->Int) |] ) nat'woPred = $(p [| (0 :: Int, succ :: Int->Int, nat_cata :: (->) Int (a -> (a -> a) -> a)) |] ) natural' = $(p [| (0 :: Integer, succ :: Integer->Integer, nat_cata :: (->) Integer (a -> (a -> a) -> a), pred :: Integer->Integer) |] ) -- Nat paramorphism nat_para :: Integral i => i -> a -> (i -> a -> a) -> a nat_para i x f = np (abs i) -- Version 0.8 does not deal with partial functions very well. where np 0 = x np i = let i' = i-1 in f i' (np i') -- Nat paramorphism. nat_cata i x f == iterate f x `genericIndex` abs i holds, but the following implementation is much more efficient (and thus safer). nat_cata :: Integral i => i -> a -> (a -> a) -> a nat_cata i x f = nc (abs i) -- Version 0.8 does not deal with partial functions very well. where nc 0 = x nc i = f (nc (i-1)) list'' = $(p [| ([], (:), flip . flip foldr :: a -> (->) [b] ((b -> a -> a) -> a), tail :: [a] -> [a]) |] ) -- foldr's argument order makes the synthesis slower:) list' = $(p [| ([], (:), foldr :: (b -> a -> a) -> a -> (->) [b] a, tail :: [a] -> [a]) |] ) -- foldr's argument order makes the synthesis slower:) list = $(p [| ([], (:), list_para :: (->) [b] (a -> (b -> [b] -> a -> a) -> a)) |] ) -- List paramorphism list_para :: [b] -> a -> (b -> [b] -> a -> a) -> a list_para [] x f = x list_para (y:ys) x f = f y ys (list_para ys x f) bool = $(p [| (True, False, iF :: (->) Bool (a -> a -> a)) |] ) iF :: Bool -> a -> a -> a iF True t f = t iF False t f = f -- | 'postprocess' replaces uncommon functions like catamorphisms with well-known functions. postprocess :: Exp -> Exp postprocess (AppE (AppE (AppE (InfixE (Just e1) (VarE name) (Just e2)) e3) e4) e5) | nameBase name == "." = postprocess $ ((e1 `AppE` (e2 `AppE` e3)) `AppE` e4) `AppE` e5 -- ad hoc pattern:S -- postprocess (AppE (AppE (AppE (AppE (VarE name) e1) e2) e3) e4) | nameBase name == "flip" = postprocess $ ((e1 `AppE` e3) `AppE` e2) `AppE` e4 postprocess (AppE (e@(AppE (AppE (VarE name) p) t)) f) = case nameBase name of "iF" -> CondE ppp ppt ppf "enumFromThenTo" -> ArithSeqE $ FromThenToR ppp ppt ppf "nat_cata" -> InfixE (Just $ (VarE (mkName "iterate") `AppE` ppf) `AppE` ppt) (VarE (mkName "!!")) -- Should I use genericIndex instead of (!!) also here? (Just $ case ppp of LitE (IntegerL i) -> LitE $ IntegerL $ abs i _ -> VarE (mkName "abs") `AppE` ppp) "flip" -> postprocess $ case ppp of VarE nm -> (VarE (mkName $ nameBase nm) `AppE` ppf) `AppE` ppt -- can't recall why, but the name needs to be unqualified _ -> (ppp `AppE` ppf) `AppE` ppt "." -> postprocess (p `AppE` (t `AppE` f)) _ -> postprocess e `AppE` ppf where ppp = postprocess p ppt = postprocess t ppf = postprocess f postprocess (AppE f@(AppE (VarE name) lj) e) = case nameBase name of "drop" -> case pplj of LitE (IntegerL j) | j<=0 -> ppe | j> 0 -> ppdrop j e _ -> (dropE `AppE` pplj) `AppE` ppe "enumFromTo" -> ArithSeqE $ FromToR pplj ppe _ -> postprocess f `AppE` ppe where pplj = postprocess lj ppe = postprocess e postprocess (AppE v@(VarE name) e) = case nameBase name of "tail" -> ppdrop 1 e "negate" -> case ppe of LitE (IntegerL i) -> LitE $ IntegerL $ (-i) LitE (RationalL r) -> LitE $ RationalL $ (-r) _ -> AppE v ppe "succ" -> case ppe of LitE (IntegerL i) -> LitE $ IntegerL $ succ i LitE (RationalL r) -> LitE $ RationalL $ succ r LitE (CharL c) -> LitE $ CharL $ succ c InfixE (Just (LitE (IntegerL n))) (VarE nm) (Just e) | nameBase nm == "+" -> InfixE (Just $ LitE $ IntegerL $ succ n) plusE (Just e) AppE (VarE nm) e | nameBase nm == "succ" -> InfixE (Just $ LitE $ IntegerL 2) plusE (Just e) -- This is OK, if we use succ only for numbers. _ -> AppE (ppv v) ppe _ -> AppE (ppv v) ppe where ppe = postprocess e -- The following pattern is actually unnecessary if only eta-long normal expressions will be generated. postprocess e@(VarE _) = ppv e postprocess (AppE f x) = postprocess f `AppE` postprocess x postprocess (InfixE me1 op me2) = case (j1,op,j2) of (Just (LitE (IntegerL i1)), VarE opname, Just (LitE (IntegerL i2))) -> case nameBase opname of "+" -> LitE $ IntegerL $ i1+i2 "-" -> LitE $ IntegerL $ i1-i2 "*" -> LitE $ IntegerL $ i1*i2 _ -> theDefault _ -> theDefault where j1 = fmap postprocess me1 j2 = fmap postprocess me2 theDefault = InfixE j1 op j2 postprocess (LamE pats e) = ppLambda pats (postprocess e) postprocess (TupE es) = TupE (map postprocess es) postprocess (ListE es) = ListE (map postprocess es) postprocess (SigE e ty) = postprocess e `SigE` ty postprocess e = e shown `appearsIn` e = G.everything (||) (False `G.mkQ` (\name -> show (name::Name) == shown)) e -- この辺はCoreLangでやるべきという気も.少なくとも,そっちで関数を定義すべき. -- \x -> iF foo bar x の場合も先にη簡約されてしまうとイマイチではある.ので,η簡約はiF, nat_cata, tailなどの処理の後にやる. -- For readability, we apply eta-reduction only when we can fully eta-reduce at the outermost lambda-abstraction. ppLambda [VarP n] (AppE e (VarE n')) | shown == show n' && not (shown `appearsIn` e) = e where shown = show n ppLambda [VarP n, VarP m] (AppE (AppE e (VarE n')) (VarE m')) | shown == show n' && showm == show m' && free = e | shown == show m' && showm == show n' && free = flipE `AppE` e where shown = show n showm = show m free = not (shown `appearsIn` e) && not (showm `appearsIn` e) -- postprocess (LamE [WildP] e) = constE `AppE` e -- not sure if this is more readable.... ppLambda [VarP n, WildP] (VarE n') | show n == show n' = constE ppLambda [VarP n] (VarE n') | show n == show n' = VarE (mkName "id") ppLambda [VarP n, VarP m] (InfixE (Just (VarE n')) op (Just (VarE m'))) | show n == show n' && show m == show m' = op ppLambda pats@[VarP n, VarP m] e@(InfixE (Just (VarE n')) op@(VarE opna) (Just (VarE m'))) = if show n == show m' && show m == show n' then case nameBase opna of "<" -> VarE (mkName ">") "<=" -> VarE (mkName ">=") "-" -> VarE (mkName "subtract") name | name `elem` ["==","/=","+","*","&&","||"] -> op | otherwise -> flipE `AppE` op else LamE pats e ppLambda [VarP n] (InfixE (Just (VarE n')) op (Just e)) | shown == show n' && not (shown `appearsIn` e) = case op of VarE name | nameBase name == "-" -> VarE (mkName "subtract") `AppE` e _ -> InfixE Nothing op (Just e) where shown = show n ppLambda [VarP n] (InfixE (Just e) op (Just (VarE n'))) | shown == show n' && not (shown `appearsIn` e) = InfixE (Just e) op Nothing where shown = show n ppLambda pats e = LamE pats e ppv e@(VarE name) | nameBase name `elem` ["iF", "nat_cata"] = LamE [ VarP n | n <- names ] (postprocess (AppE (AppE (AppE e p) t) f)) | otherwise = e where names = [ mkName [n] | n <- "ptf" ] [p,t,f] = map VarE names ppdrop m0j e = case postprocess e of AppE (AppE (VarE drn) (LitE (IntegerL i))) list | nameBase drn == "drop" -> droppy (m0j + i) list -- NB: m0j and i are both positive. ppe -> droppy m0j ppe where droppy i e = (dropE `AppE` (LitE $ IntegerL i)) `AppE` e constE = VarE $ mkName "const" flipE = VarE $ mkName "flip" plusE = VarE $ mkName "+" dropE = VarE $ mkName "drop" procSucc n (AppE (VarE name) e) | nameBase name == "succ" = procSucc (n+1) e procSucc n (LitE (CharL c)) = LitE $ CharL $ iterate succ c `genericIndex` n procSucc n (LitE (IntegerL i)) = LitE $ IntegerL $ n+i procSucc n (LitE (RationalL r)) = LitE $ RationalL $ fromInteger n + r procSucc n e = InfixE (Just $ LitE $ IntegerL n) (VarE $ mkName "+") (Just $ postprocess e) -- This is OK, if we use succ only for numbers. postprocessQ :: Exp -> ExpQ {- This type of patterns is not available yet. postprocessQ (AppE (AppE (AppE (VarE 'iF) p) t) f) = [| if $(postprocessQ p) then $(postprocessQ t) else $(postprocessQ f) |] postprocessQ (AppE (AppE (AppE (VarE 'nat_para) i) x) f) = [| let {np 0 = $(postprocessQ x); np (n+1) = $(postprocessQ f) n (np n)} in np (abs $(postprocessQ i)) |] postprocessQ (AppE (AppE (AppE (VarE 'list_para) xs) x) f) = [| let {lp [] = $(postprocessQ x); lp (y:ys) = $(postprocessQ f) y ys (lp ys)} in lp $(postprocessQ xs) |] -} postprocessQ (AppE (e@(AppE (AppE (VarE name) p) t)) f) = case nameBase name of "iF" -> [| if $(postprocessQ p) then $(postprocessQ t) else $(postprocessQ f) |] "nat_cata" -> [| iterate $(postprocessQ f) $(postprocessQ t) !! abs $(postprocessQ p) |] "nat_para" -> [| let {np 0 = $(postprocessQ t); np n = let i=n-1 in $(postprocessQ f) i (np i)} in np (abs $(postprocessQ p)) |] "list_para" -> [| let {lp [] = $(postprocessQ t); lp (y:ys) = $(postprocessQ f) y ys (lp ys)} in lp $(postprocessQ p) |] _ -> [| $(postprocessQ e) $(postprocessQ f) |] postprocessQ (AppE f x) = [| $(postprocessQ f) $(postprocessQ x) |] -- postprocessQ (VarE 'iF) = [| \p t f -> if p then t else f |] -- This pattern is actually unnecessary because only eta-long normal expressions will be generated. -- ... postprocessQ (InfixE me1 op me2) = let fmapM f Nothing = return Nothing fmapM f (Just x) = fmap Just (f x) in liftM2 (\e1 e2 -> InfixE e1 op e2) (fmapM postprocessQ me1) (fmapM postprocessQ me2) postprocessQ (LamE pats e) = fmap (LamE pats) (postprocessQ e) postprocessQ (TupE es) = fmap TupE (mapM postprocessQ es) postprocessQ (ListE es) = fmap ListE (mapM postprocessQ es) postprocessQ (SigE e ty) = fmap (`SigE` ty) (postprocessQ e) postprocessQ e = return e exploit :: (Typeable a, Filtrable a) => (a -> Bool) -> IO () exploit pred = filterThenF pred (everything (reallyall::ProgGenSF)) >>= pprs boolean = $(p [| ((&&) :: Bool -> Bool -> Bool, (||) :: Bool -> Bool -> Bool, not :: Bool -> Bool) |] ) -- Type classes are not supported yet.... -- Without tuning of the probability distribution over Chars and Lists, these are almost useless. eq = $(p [| ((==) :: Int->Int->Bool, (/=) :: Int->Int->Bool, (==) :: Char->Char->Bool, (/=) :: Char->Char->Bool, (==) :: Bool->Bool->Bool, (/=) :: Bool->Bool->Bool, (==) :: [Int] ->[Int] ->Bool, (/=) :: [Int] ->[Int]->Bool, (==) :: [Char]->[Char]->Bool, (/=) :: [Char]->[Char]->Bool, (==) :: [Bool]->[Bool]->Bool, (/=) :: [Bool]->[Bool]->Bool) |] ) -- ...bothered. {- eq = $(p [| ((==) :: Int->Int->Bool, (/=) :: Int->Int->Bool, (==) :: Char->Char->Bool, (/=) :: Char->Char->Bool, (==) :: Bool->Bool->Bool, (/=) :: Bool->Bool->Bool) |]) -} intinst = intinst1++intinst2 intinst1 = $(p [| ( (<=) :: Int->Int->Bool, (<) :: Int->Int->Bool, -- (>=) :: Int->Int->Bool, -- (>) :: Int->Int->Bool, max :: Int->Int->Int, min :: Int->Int->Int, (-) :: Int->Int->Int, (*) :: Int->Int->Int, div :: Int->Int->Int, mod :: Int->Int->Int, (^) :: Int->Int->Int) |]) intinst2 = $(p [| ( gcd :: Int->Int->Int, lcm :: Int->Int->Int) |]) list1 = $(p [| (map :: (a -> b) -> (->) [a] [b], (++) :: [a] -> [a] -> [a], filter :: (a -> Bool) -> [a] -> [a], concat :: [[a]] -> [a], concatMap :: (a -> [b]) -> (->) [a] [b], length :: (->) [a] Int, replicate :: Int -> a -> [a], take :: Int -> [a] -> [a], drop :: Int -> [a] -> [a], takeWhile :: (a -> Bool) -> [a] -> [a], dropWhile :: (a -> Bool) -> [a] -> [a]) |] ) list1' = $(p [| (flip map :: (->) [a] ((a -> b) -> [b]), (++) :: [a] -> [a] -> [a], filter :: (a -> Bool) -> [a] -> [a], concat :: [[a]] -> [a], flip concatMap :: (->) [a] ((a -> [b]) -> [b]), length :: (->) [a] Int, replicate :: Int -> a -> [a], take :: Int -> [a] -> [a], drop :: Int -> [a] -> [a], takeWhile :: (a -> Bool) -> [a] -> [a], dropWhile :: (a -> Bool) -> [a] -> [a]) |] ) list2 = $(p [| ( lines :: [Char] -> [[Char]], words :: [Char] -> [[Char]], unlines :: [[Char]] -> [Char], unwords :: [[Char]] -> [Char] ) |] ) list3 = $(p [| (reverse :: [a] -> [a], and :: [Bool] -> Bool, or :: [Bool] -> Bool, any :: (a -> Bool) -> (->) [a] Bool, all :: (a -> Bool) -> (->) [a] Bool, zipWith :: (a->b->c) -> (->) [a] ((->) [b] [c]) ) |] ) list3' = $(p [| (reverse :: [a] -> [a], and :: [Bool] -> Bool, or :: [Bool] -> Bool, flip any :: (->) [a] ((a -> Bool) -> Bool), flip all :: (->) [a] ((a -> Bool) -> Bool), flip . flip zipWith :: (->) [a] ((->) [b] ((a->b->c) -> [c])) ) |] ) nats = $(p [| (1 ::Int, 2 :: Int, 3 :: Int) |]) reallyall :: ProgramGenerator pg => pg reallyall = mkPG rich nrnds = repeat 5 -- comment out (mkStdGen 123456) when using 0.8.3* -- Currently only the pg==ConstrLSF case makes sense. mix, poormix :: ProgramGenerator pg => pg mix = mkPGSF (mkStdGen 123456) nrnds (list++bool) rich -- I think having both succ and pred is not good, and pred x can be synthesized as x - succ 0. -- Still, having both cons and tail is OK. soso = (list'' ++ nat'woPred ++ mb' ++ bool ++ $(p [| (+) :: Int -> Int -> Int |]) ++ -- x $(p [| (hd :: [a] -> Maybe a, (+) :: Int -> Int -> Int) |]) ++ boolean ++ eq ++ intinst1 ++ list1' ++ list3') rich = soso ++ list2 ++ intinst2 ++ $(p [| init :: [a] -> [a] |]) poormix = mkPGSF (mkStdGen 123456) nrnds $(p [| ([] :: [a], True) |] ) rich -- just for debugging ra :: ProgramGenerator pg => pg ra = mkPG rich' rich' = (list'++bool++boolean++ list1 ++ list3) mx :: ProgramGenerator pg => pg mx = mkPGSF (mkStdGen 123456) nrnds (list++bool) rich' debug = $(p [| (list_para :: (->) [b] (a -> (b -> [b] -> a -> a) -> a), concatMap :: (a -> [b]) -> (->) [a] [b]) |] ) -- Library used by the program server backend pgfull :: ProgGenSF -- pgfull = mkPG ($(MagicHaskeller.LibTH.load "libsrc/PreludeList.hs") ++ mb ++ bool ++ boolean ++ $(p [| ([], (:), (+) :: Int -> Int -> Int, replicate :: Int -> a -> [a]) |]) ++ $(p [| until :: (a -> Bool) -> (a -> a) -> a -> a |]) ++ nat ++ eq ++ intinst) -- rich 障紊鐚 pgfull = mkPGXOpt options{tv1=True,nrands=repeat 20} $ foldr (zipWith (++)) literals [fromPrelude, fromDataList, fromDataChar, fromDataMaybe] literals = [$(p [|(1::Int, ' '::Char)|]),[],[]] fromPrelude = [soso ++ $(p [| (abs :: Int->Int, compare :: Char->Char->Ordering, compare :: Int->Int->Ordering, foldr const :: a -> [a] -> a, flip (flip . either) :: (->) (Either a b) ((a -> c) -> (b -> c) -> c)) |]), list2 ++ $(p [| (scanl :: (a -> b -> a) -> a -> [b] -> [a], scanr :: (a -> b -> b) -> b -> [a] -> [b], scanl1 :: (a -> a -> a) -> [a] -> [a], scanr1 :: (a -> a -> a) -> [a] -> [a], replicate :: Int -> a -> [a], sum :: [Int]->Int, product :: [Int]->Int, -- until :: (a -> Bool) -> (a -> a) -> a -> a) ャ鐚untilャ鐚蚊鐚鋎帥鐚篏 enumFromTo :: Int->Int->[Int], show :: Int -> String) |])++ $(p [| flip uncurry :: (->) (a,b) ((a->b->c) -> c) |]), intinst2 ++ $(p [| ((,) :: a -> b -> (a,b), Left :: a -> Either a b, Right :: b -> Either a b) |])] -- , enumFromThenTo :: Int->Int->Int->[Int]) |])] -- The problem is that enumFromThenTo 1 1 2 is infinite. fromDataList = [$(p [| (sortBy, nubBy, deleteBy, transpose, stripPrefix :: String->String->Maybe String)|]), $(p [| ( find, flip findIndex :: (->) [a] ((a -> Bool) -> Maybe Int), flip findIndices :: (->) [a] ((a -> Bool) -> [Int]), deleteFirstsBy, unionBy, intersectBy, groupBy, insertBy, maximumBy, minimumBy) |]), $(p [| (intersperse, subsequences, permutations, -- dropWhileEnd, inits, tails, isPrefixOf :: String -> String -> Bool, isSuffixOf :: String -> String -> Bool, isInfixOf :: String -> String -> Bool ) |])] fromDataChar = [$(p [| (toUpper, toLower) |]), $(p [| (ord, chr, digitToInt, intToDigit, isControl, isSpace, isLower, isUpper, isAlpha, isAlphaNum, isDigit) |]), $(p [| (isPrint, isOctDigit, isHexDigit) |])] fromDataMaybe = [[], $(p [| (catMaybes, listToMaybe, maybeToList) |])] -- mapMaybe f = catMaybes . map f