packages feed

dragen (empty) → 0.1.0.0

raw patch · 15 files changed

+1686/−0 lines, 15 filesdep +QuickCheckdep +basedep +containerssetup-changed

Dependencies added: QuickCheck, base, containers, dragen, extra, ghc-prim, matrix, split, template-haskell, text, transformers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Agustín Mista (c) 2018++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Author name here nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,16 @@+![](img/logo.png)++# DRAGEN - Derivation of Random Generators++To test the tool please run:++```+$ stack setup+$ stack build+$ stack test+```++Please make sure you have `BLAS` and `LAPACK` installed in your system before compiling.++The predictions can be confirmed averaging a large set of generated values.+See file `test/Examples.hs` for an example of this.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dragen.cabal view
@@ -0,0 +1,70 @@+cabal-version: 1.12+name: dragen+version: 0.1.0.0+license: BSD3+license-file: LICENSE+copyright: 2018 Agustín Mista+maintainer: Agustín Mista+author: Agustín Mista+homepage: https://github.com/OctopiChalmers/dragen#readme+bug-reports: https://github.com/OctopiChalmers/dragen/issues+synopsis: Automatic derivation of optimized QuickCheck random generators.+description:+    DRAGEN is a Template Haskell tool for automatically deriving QuickCheck generators in compile-time. The user sets a desired distribution of values, and DRAGEN will try optimize the generation parameters to satisfy it using probabilistic analyses based on multi-type branching processes.+    DRAGEN is based on the following paper+    Branching processes for QuickCheck generators. Agustín Mista, Alejandro Russo, John Hughes. Haskell Symposium, 2018. https://dl.acm.org/citation.cfm?doid=3242744.3242747+category: Testing+build-type: Simple+extra-source-files:+    README.md++source-repository head+    type: git+    location: https://github.com/OctopiChalmers/dragen++library+    exposed-modules:+        Countable+        Dragen+        TypeInfo+        Reification+        Prediction+        Optimization+        Arbitrary+        Megadeth+    hs-source-dirs: src+    other-modules:+        Paths_dragen+    default-language: Haskell2010+    build-depends:+        QuickCheck >=2.11.3 && <2.12,+        base >=4.7 && <5,+        containers >=0.5.11.0 && <0.6,+        extra >=1.6.9 && <1.7,+        ghc-prim >=0.5.2.0 && <0.6,+        matrix >=0.3.6.1 && <0.4,+        split >=0.2.3.3 && <0.3,+        template-haskell >=2.13.0.0 && <2.14,+        transformers >=0.5.5.0 && <0.6++test-suite examples+    type: exitcode-stdio-1.0+    main-is: Main.hs+    hs-source-dirs: test/+    other-modules:+        Examples+        TestCountable+        Paths_dragen+    default-language: Haskell2010+    build-depends:+        QuickCheck >=2.11.3 && <2.12,+        base >=4.7 && <5,+        containers >=0.5.11.0 && <0.6,+        dragen -any,+        extra >=1.6.9 && <1.7,+        ghc-prim >=0.5.2.0 && <0.6,+        matrix >=0.3.6.1 && <0.4,+        split >=0.2.3.3 && <0.3,+        template-haskell >=2.13.0.0 && <2.14,+        text >=1.2.3.0 && <1.3,+        transformers >=0.5.5.0 && <0.6
+ src/Arbitrary.hs view
@@ -0,0 +1,200 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE LambdaCase #-}++module Arbitrary where++import Data.Maybe+import Data.List+import Data.Map.Strict ((!)) +import qualified Data.Map.Strict as Map++import Test.QuickCheck+import Language.Haskell.TH+import Language.Haskell.TH.Syntax as TH++import Megadeth+import TypeInfo+import Prediction+++customListGen :: Arbitrary t => Int -> Int -> Gen [t]+customListGen fnil fcons = sized go+  where+    go 0 = return []+    go n = frequency+      [ (fnil, return [])+      , (fcons, (:) <$> resize (n-1) arbitrary <*> go (n-1)) ]++customMaybeGen :: Arbitrary t => Int -> Int -> Gen (Maybe t)+customMaybeGen fnothing fjust = sized go+  where+    go 0 = return Nothing+    go n = frequency+      [ (fnothing, return Nothing)+      , (fjust, Just <$> resize (n-1) arbitrary)+      ]++chooseExpQ :: FreqMap -> Name -> Name -> Name -> Bool -> TH.Type -> ExpQ+chooseExpQ freqs goName nName target recursive ty+  | not recursive+    = [| arbitrary |]+  | headOf ty == target+    = [| $(varE goName) (max 0 ($(varE nName) - 1)) |]+     -- For the case of lists we use a custom generator, since its Arbitrary+     -- instance does not preserve generation frequencies on each iteration.+     -- Dirty hack to dodge Haskell's typeclasses system. This should be+     -- generalized to any type having this issue.+  | headOf ty == ''[]+    = let (fnil, fcons) = (freqs ! '[], freqs ! '(:)) in+      [| resize (max 0 ($(varE nName) - 1)) (customListGen fnil fcons) |]+     -- We need to do this hack for Maybe too. Clearly, this does not scale at+     -- all and we should refactor the code making it independent of the+     -- Arbitrary typeclass.+  | headOf ty == ''Maybe+    = let (fnothing, fjust) = (freqs ! 'Nothing, freqs ! 'Just) in+      [| resize (max 0 ($(varE nName) - 1)) (customMaybeGen fnothing fjust) |]+  | otherwise+    = [| resize (max 0 ($(varE nName) - 1)) arbitrary |]+++makeArbExpsQ :: FreqMap -> Name -> Name -> Name ->  [ConView] -> [ExpQ]+makeArbExpsQ freqs goName nName targetName cons+  = map (fmap fixAppl)+    [ foldl (applyTParam rec) (conE conName) conArgs+    | SimpleCon conName rec conArgs <- cons ]+  where+    applyTParam rec rem param = rem `infixAppE` (chooseExp rec param)+    chooseExp rec = chooseExpQ freqs goName nName targetName rec+    infixAppE l r = uInfixE l (varE '(<*>)) r++frequencyExpQ :: FreqMap -> Name -> Name -> Name ->  [ConView] -> ExpQ+frequencyExpQ freqs goName nName target cons+  = [| frequency $(listE tuples) |]+  where+    tuples = map (\(f,g) -> tupE [f,g]) (zip freqExpsQ arbExpsQ)+    freqExpsQ = map getFreqExpQ cons+    arbExpsQ = makeArbExpsQ freqs goName nName target cons+    getFreqExpQ con = maybe [|1|] (\f->[|f|]) (Map.lookup (nm con) freqs)+++genTupleArbs :: Int -> ExpQ+genTupleArbs n = doE $+  map (\x -> bindS (varP x) (varE 'arbitrary)) vars +++  [ noBindS $ appE (varE 'return) (tupE (map varE vars))]+    where vars = take n varNames++isMutRec :: TypeEnv -> ConView -> Bool+isMutRec env con = nm con `elem` recs+  where recs = map cname (getRecursives env)++updateMutRec :: TypeEnv -> ConView -> ConView+updateMutRec env con+    | isMutRec env con = con { recursive = True }+    | otherwise = con+++deriveArbitraryInstance :: TypeEnv -> FreqMap -> Name -> Q [Dec]+deriveArbitraryInstance env freqs target = reify target >>= \case++  {- data T {...} = C1 {...} | C2 {...} | ... -}+  TyConI (DataD _ _ params _ cons _) -> do+    let paramExps = map varT (paramNames params)+        allCons = map (updateMutRec env . simpleConView target) cons+        (recCons, termCons) = partition recursive allCons++        mkGo goName nName+          | length allCons == 1+            = head (makeArbExpsQ freqs goName nName target allCons)+          | length recCons == length allCons+            = frequencyExpQ freqs goName nName target recCons+          | length termCons == 1+            = condE [| $(varE nName) == 0 |]+                (head (makeArbExpsQ freqs goName nName target termCons))+                (frequencyExpQ freqs goName nName target allCons)+          | otherwise+            = condE [| $(varE nName) == 0 |]+                (frequencyExpQ freqs goName nName target termCons)+                (frequencyExpQ freqs goName nName target allCons)++    if not (null paramExps)+    then+      [d|+      instance $(applyTo (tupleT (length paramExps))+                         (map (appT (conT ''Arbitrary)) paramExps))+            => Arbitrary $(applyTo (conT target) paramExps) where+        arbitrary = sized go+          where+            go n = $(mkGo 'go 'n)+      |]+    else+      [d|+      instance Arbitrary $(applyTo (conT target) paramExps) where+        arbitrary = sized go+          where+            go n = $(mkGo 'go 'n)+      |]+++  {- newtype T {...} = SingleCon {...} -}+  TyConI (NewtypeD _ _ params _ con _) -> do+    let paramExps = map varT (paramNames params)+        singleCon = simpleConView target con++    if not (null paramExps)+    then+      [d|+      instance $(applyTo (tupleT (length paramExps))+                         (map (appT (conT ''Arbitrary)) paramExps))+            => Arbitrary $(applyTo (conT target) paramExps) where+        arbitrary = sized go+          where go n = $(head (makeArbExpsQ freqs 'go 'n target [singleCon]))+      |]+    else+      [d|+      instance Arbitrary $(applyTo (conT target) paramExps) where+        arbitrary = sized go+          where+            go n = $(head (makeArbExpsQ freqs 'go 'n target [singleCon]))+      |]+++  {- type T {...} = U {...} -}+  TyConI (TySynD _ params ty) ->+    case (getTy ty) of++      {- type T {...} = ({...}, {...}, ...) -}+      (TupleT n) -> do+        let paramExps = map varT (paramNames params)++        if not (null paramExps)+        then+          [d|+          instance $(applyTo (tupleT (length paramExps))+                             (map (appT (conT ''Arbitrary)) paramExps))+                => Arbitrary $(applyTo (conT target) paramExps) where+            arbitrary = $(genTupleArbs n)+          |]+        else+          [d|+          instance Arbitrary $(applyTo (conT target) paramExps) where+            arbitrary = $(genTupleArbs n)+          |]++      -- This type should had been derived already, It is clearly a+      -- dependency and it should be put before in the topsort.+      (ConT n) -> return []++      _ -> runIO (putStrLn ("IGNORING: " ++ show ty)) >> return []+++  {- Int#, Bool#, ...  -}+  PrimTyConI {} -> return []+++  {- Not supported yet. ([], (,), ...) -}+  x -> error ("Case not defined: " ++ show x)+++devArbitrary :: TypeEnv -> FreqMap -> Name -> Q [Dec]+devArbitrary env freqs+  = megaderive (deriveArbitraryInstance env freqs) (isInsName ''Arbitrary)
+ src/Countable.hs view
@@ -0,0 +1,173 @@+{-|+Module      : Countable++This file provides a generic implementation for counting how many times a data+constructor appears in a value. We use two different type classes in order to+achieve this, called `Countable` (for types of kind *) and `Countable1` (for+types of kind (* -> *)).++> class Countable (a :: *) where+>     count :: a -> ConsMap++> class Countable1 (f :: * -> *) where+>     count1 :: f a -> ConsMap++Let us suppose we have the following type definition:++> data Tree = Leaf | Node Tree Tree++if we want to count how many times each type constructor appears within a+given value of type `Tree`, we need to add the following instance+derivations:++> deriving instance Generic Tree+> instance Countable Tree++Then, we can count type constructors over values of type `Tree`:++> count (Node (Node Leaf Leaf) (Node Leaf Leaf))+> ==> fromList [("Leaf",4),("Node",3)]++Note that, if the `Tree` data type definition is available, the `deriving+instance Generic Tree` could be avoided by including Generic at the+type definition deriving clause:++> data Tree = Leaf | Node Tree Tree deriving Generic++`Countable` requires every subtype of a `Countable` data type to be also+`Countable` in order to work. If we modify our `Tree` definition as++> data GTree a = GLeaf | GNode GTree a GTree++then is necessary to add the following instance derivations:++> instance deriving Generic a => Generic (GTree a)+> instance (Generic a, Countable a) => Countable (GTree a)++and the `Generic` and `Countable` derivations for whatever `a` we want to+use.  For example, let `a` be `Bool` (`Bool` already has a `Generic`+instance):++> instance Countable Bool++then we can count type constructors on values of type `GTree Bool`++> count (GNode (GNode GLeaf False GLeaf) True (GNode GLeaf True GLeaf))+> ==> fromList [("False",1),("GLeaf",4),("GNode",3),("True",2)]++but what if we are just interested in counting `GLeaf` and `GNode`+type constructors within values of type `GTree a`? Using `Countable` type+class would require to provide (or derive) proper `Generic` and `Countable`+instances for whatever type we instantiate `a` with. Fortunately, we can+define a new type class, `Countable1`, for types of kind (* -> *) that does+not count type constructors further than the outter data type. Later, we+derive a `Countable1` instance for `GTree`.++> instance Countable1 GTree++> count1 (GNode (GNode GLeaf 1 GLeaf) 2 (GNode GLeaf 3 GLeaf))+> ==> fromList [("GLeaf",4),("GNode",3)]++> count1 (GNode (GNode GLeaf "a" GLeaf) "b" (GNode GLeaf "c" GLeaf))+> ==> fromList [("GLeaf",4),("GNode",3)]+-}++{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances  #-}+{-# LANGUAGE KindSignatures  #-}+{-# LANGUAGE DefaultSignatures  #-}+{-# LANGUAGE TypeOperators  #-}++module Countable where++import GHC.Generics++import Data.Map.Strict (Map, (!))+import qualified Data.Map.Strict as Map+++-- | A map that associates constructors names and the times each one appears+-- within a value.+type ConsMap = Map String Int++class Countable (a :: *) where+    count :: a -> ConsMap++    default count :: (Generic a, GCountable (Rep a)) => a -> ConsMap+    count = gcount . from+++class GCountable f where+    gcount :: f a -> ConsMap+++instance GCountable (URec a) where+    gcount _ = Map.empty++instance GCountable V1 where+    gcount _ = Map.empty++instance GCountable U1 where+    gcount U1 = Map.empty++instance Countable a => GCountable (K1 i a) where+    gcount (K1 x) = count x++instance (GCountable f, GCountable g) => GCountable (f :*: g) where+    gcount (f :*: g)  = Map.unionWith (+) (gcount f) (gcount g)++instance (GCountable f, GCountable g) => GCountable (f :+: g) where+    gcount (L1 x) = gcount x+    gcount (R1 x) = gcount x++instance (Constructor c, GCountable f) => GCountable (C1 c f) where+    gcount cons@(M1 inner) = Map.unionWith (+)+        (Map.singleton (conName cons) 1) (gcount inner)++instance GCountable f => GCountable (D1 c f) where+    gcount (M1 x) = gcount x++instance GCountable f => GCountable (S1 c f) where+    gcount (M1 x) = gcount x++--------------------------------------------------------------------------------++class Countable1 (f :: * -> *) where+    count1 :: f a -> ConsMap++    default count1 :: (Generic1 f, GCountable1 (Rep1 f)) => f a -> ConsMap+    count1 = gcount1 . from1+++class GCountable1 f where+    gcount1 :: f a -> ConsMap+++instance GCountable1 V1 where+    gcount1 _ = Map.empty++instance GCountable1 U1 where+    gcount1 U1 = Map.empty++instance GCountable1 Par1 where+    gcount1 (Par1 _) = Map.empty++instance (Countable1 f) => GCountable1 (Rec1 f) where+    gcount1 (Rec1 a) = count1 a++instance (GCountable1 f, GCountable1 g) => GCountable1 (f :*: g) where+    gcount1 (f :*: g)  = Map.unionWith (+) (gcount1 f) (gcount1 g)++instance (GCountable1 f, GCountable1 g) => GCountable1 (f :+: g) where+    gcount1 (L1 x) = gcount1 x+    gcount1 (R1 x) = gcount1 x++instance (Constructor c, GCountable1 f) => GCountable1 (C1 c f) where+    gcount1 cons@(M1 inner) = Map.unionWith (+)+        (Map.singleton (conName cons) 1) (gcount1 inner)++instance GCountable1 f => GCountable1 (D1 c f) where+    gcount1 (M1 x) = gcount1 x++instance GCountable1 f => GCountable1 (S1 c f) where+    gcount1 (M1 x) = gcount1 x
+ src/Dragen.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE BangPatterns #-}++module Dragen+( dragenArbitrary+, Optimization.uniform+, Optimization.weighted+, Optimization.only+, Optimization.without+, Optimization.types+, Optimization.constructors+, Prediction.confirm+) where++import Language.Haskell.TH++import Reification+import TypeInfo+import Prediction+import Optimization+import Arbitrary+++-- | Derives an Abitrary instance for the type `target`, optimizing each type+-- constructor frequency in order to minimize the output of a given cost+-- function.+dragenArbitrary  :: Name -> Size -> CostFunction -> DecsQ+dragenArbitrary target size cost = do++  let putStrLnQ = runIO . putStrLn++  putStrLnQ $ "\nReifiying: " ++ show target++  targetEnv <- reifyNameEnv target++  putStrLnQ $ "\nTypes involved with " ++ show target ++ ":"+  putStrLnQ $ show (map tsig targetEnv)++  let !freqMap = initMap targetEnv+      !prediction = predict targetEnv size freqMap+      !initCost = cost targetEnv size freqMap++  putStrLnQ $ "\nInitial frequencies map:"+  putStrLnQ $ showMap freqMap+  putStrLnQ $ "\nPredicted distribution for the initial frequencies map:"+  putStrLnQ $ showMap prediction++  putStrLnQ $ "\nOptimizing the frequencies map:"+  let !optimized = optimizeLS targetEnv size cost freqMap+      !prediction' = predict targetEnv size optimized+      !finalCost = cost targetEnv size optimized++  putStrLnQ $ "\n\nOptimized frequencies map:"+  putStrLnQ $ showMap optimized+  putStrLnQ $ "\nPredicted distribution for the optimized frequencies map:"+  putStrLnQ $ showMap prediction'++  putStrLnQ $ "\nInitial cost: " ++ show initCost+  putStrLnQ $ "Final cost: " ++ show finalCost+  putStrLnQ $ "Optimization ratio: " ++ show (initCost / finalCost)++  putStrLnQ $ "\nDeriving optimized generator..."+  devArbitrary targetEnv optimized target
+ src/Megadeth.hs view
@@ -0,0 +1,245 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ViewPatterns #-}++module Megadeth where++import Data.List+import Control.Monad+import qualified Data.Map.Strict as M+import qualified Data.Graph as G++import qualified Control.Monad.Trans.Class as TC+import Control.Monad.Trans.State.Lazy++import Language.Haskell.TH+import Language.Haskell.TH.Syntax++-- TH 2.11 introduced kind type+#if MIN_VERSION_template_haskell(2,11,0)+#    define TH211MBKIND _maybe_kind+#else+#    define TH211MBKIND+#endif++-- | View Pattern for Types+data ConView = SimpleCon+     { nm :: Name+     , recursive :: Bool+     , tt :: [Type]+     } deriving Show++isRecursive :: Name -> Type -> Bool+isRecursive target (ForallT _ _ t) = isRecursive target t+isRecursive target (AppT l r) = isRecursive target l || isRecursive target r+isRecursive target (SigT t _) = isRecursive target t+isRecursive target (ConT t) = t == target+isRecursive _ _ = False++varNames = map (mkName . ('a':) . show) [0..]++paramNames :: [TyVarBndr] -> [Name]+paramNames = map f+  where f (PlainTV n) = n+        f (KindedTV n _) = n++applyTo :: TypeQ -> [TypeQ] -> TypeQ+applyTo = foldl appT++fixAppl :: Exp -> Exp+fixAppl (UInfixE e1@UInfixE {} op e2) = UInfixE (fixAppl e1) op e2+fixAppl (UInfixE con op e) = UInfixE con (VarE '(<$>)) e+fixAppl e = AppE (VarE 'return) e+++-- | Look up  the first type name in a type structure.+-- This function is not complete, so it could fail and it will+-- with an error message with the case that is missing+headOf :: Type -> Name+headOf (AppT ArrowT e) = headOf e+headOf (AppT ty1 _) = headOf ty1+headOf (SigT ty _) = headOf ty+headOf (ConT n) = n+headOf (VarT n) = n+headOf (TupleT n) = tupleTypeName n+headOf ListT = ''[]+headOf e = error ("Missing :" ++ show e)+++-- | Check whether a type is a Primitive Type.+-- Something like Int#, Bool#, etc.+isPrim :: Info -> Bool+isPrim PrimTyConI {} = True+isPrim _ = False+++-- | View Pattern for Constructors+simpleConView :: Name -> Con -> ConView+simpleConView tyName c =+  let anyRec = any (isRecursive tyName)+      proj3 (_,_,z) = z+  in case c of++    NormalC n sts -> let ts = map snd sts in SimpleCon n (anyRec ts) ts+#if MIN_VERSION_template_haskell(2,11,0)+    GadtC [n] sts _ -> let ts = map snd sts in SimpleCon n (anyRec ts) ts+#endif+    RecC n vsts -> let ts = map proj3 vsts in SimpleCon n (anyRec ts) ts++    InfixC (_,t1) n (_,t2) -> SimpleCon n (anyRec [t1] || anyRec [t2]) [t1,t2]++    ForallC _ _ innerCon -> simpleConView tyName innerCon++    _ -> error $ "simpleConView: failed on " ++ show c+++-- | Get the first type in a type application.+-- Maybe we should improve this one+getTy :: Type -> Type+getTy (AppT t _) = getTy t+getTy t = t++isVarT (VarT _) = True+isVarT _ = False++isUnit (TupleT 0) = True+isUnit _ = False++-- | Find all simple Types that are part of another Type.+findLeafTypes :: Type -> [Type]+findLeafTypes (AppT ListT ty) = findLeafTypes ty+findLeafTypes (AppT (TupleT n) ty) = findLeafTypes ty+findLeafTypes (AppT p@(ConT _) ty) = p : findLeafTypes ty+findLeafTypes (AppT ty1 ty2) = findLeafTypes ty1 ++ findLeafTypes ty2+findLeafTypes (VarT _) = []+findLeafTypes (ForallT _ _ ty) = findLeafTypes ty+findLeafTypes ArrowT = []+findLeafTypes ListT = []+findLeafTypes StarT = []+findLeafTypes ty = [ty]+++type StQ s a = StateT s Q a+type Names = [Name]++member :: Name -> StQ (M.Map Name Names) Bool+member t = do+  mk <- get+  return $ M.member t mk++addDep :: Name -> Names -> StQ (M.Map Name Names) ()+addDep n ns = do+  mapp <- get+  let newmapp = M.insert n ns mapp+  put newmapp++headOfNoVar :: Type -> [Name]+headOfNoVar (ConT n) = [n]+headOfNoVar (VarT _) = []+headOfNoVar (SigT t _ ) = headOfNoVar t+headOfNoVar (AppT ty1 ty2) = headOfNoVar ty1 ++ headOfNoVar ty2+headOfNoVar _ = []++getDeps :: Name -> (Name -> Q Bool) -> StQ (M.Map Name Names) ()+getDeps t ban = do++  visited <- member t+  b <- TC.lift (ban t)++  let cond = b || visited || hasArbIns t++  unless cond $ do++    TC.lift $ runIO (putStrLn ("Visiting:" ++ show t))+    tip <- TC.lift (reify t)++    case tip of++      TyConI (DataD _ _ _ TH211MBKIND cons _) -> do++        let inner = nub $ concat+              [ findLeafTypes ty+              | (simpleConView t -> SimpleCon _ _ tys) <- cons+              , ty <- tys+              , not (isVarT ty) ]+            hof = map headOf (filter (not . isUnit) inner)++        addDep t hof+        mapM_ getDeps' hof+++      TyConI (NewtypeD _ nm _ TH211MBKIND con _) -> do++        let (SimpleCon _ _ ts) = simpleConView nm con+            inner = nub (concatMap findLeafTypes (filter (not . isVarT) ts))+            hof = map headOf (filter (not . isUnit) inner)++        addDep t hof+        mapM_ getDeps' hof+++      TyConI (TySynD _ _ m) -> do+        addDep t (headOfNoVar m)+        mapM_ getDeps' (headOfNoVar m)++      _ -> return ()++    where getDeps' = flip getDeps ban+++tocheck :: [TyVarBndr] -> Name -> Type+tocheck bndrs nm = foldl AppT (ConT nm) ns+  where ns = map VarT (paramNames bndrs)+++hasArbIns :: Name -> Bool+hasArbIns n = let sn = show n in+      isPrefixOf "GHC." sn+  ||  isPrefixOf "Data.Text" sn+  ||  isPrefixOf "Data.Vector" sn+  ||  isPrefixOf "Data.ByteString" sn+  ||  isPrefixOf "Codec.Picture.Types" sn+  ||  isPrefixOf "Codec.Picture.Metadata.Elem" sn+  ||  isPrefixOf "Codec.Picture.Metadata.Keys" sn+--  ||  isPrefixOf "Data.Time" sn+++doPreq :: Name -> Name -> [TyVarBndr] -> Q Bool+doPreq classname n [] = fmap not (isInstance classname [ConT n])+doPreq classname n xs = fmap not (isInstance classname [tocheck xs n])+++isInsName :: Name -> Name -> Q Bool+isInsName className n = do+  inf <- reify n+  case inf of+    TyConI (DataD _ _ preq TH211MBKIND _ _) -> doPreq className n preq+    TyConI (NewtypeD _ _ preq TH211MBKIND _ _) -> doPreq className n preq+    TyConI (TySynD _ preq _ ) -> doPreq className n preq+    d -> do+      runIO $ print $ "Weird case:: " ++ show d+      doPreq className n []+++prevDev :: Name -> (Name -> Q Bool) -> Q [Name]+prevDev t ban = do+  mapp <- execStateT (getDeps t ban) M.empty+  let rs = M.foldrWithKey (\ k d ds -> (k,k,d) : ds) [] mapp+  let (graph, v2ter, f) = G.graphFromEdges rs+  let topsorted = reverse $ G.topSort graph+  return (map (\p -> (let (n,_,_) = v2ter p in n)) topsorted)+++megaderivePrim :: (Name -> Q [Dec]) -- ^ Instance generator+               -> (Name -> Q Bool)  -- ^ Blacklist dependences before+               -> (Name -> Q Bool)  -- ^ Instance name+               ->  Name -> Q [Dec]+megaderivePrim inst prefil filt t = do+    ts' <- prevDev t prefil+    ts'' <- filterM filt ts' -- Remove already known instances+    ts <- mapM inst ts''+    return $ concat ts+++megaderive :: (Name -> Q [Dec]) -> (Name -> Q Bool) -> Name -> Q [Dec]+megaderive inst = megaderivePrim inst (const $ return False)
+ src/Optimization.hs view
@@ -0,0 +1,122 @@+module Optimization where++import Data.List+import Data.Maybe+import Data.Ord+import qualified Data.Map.Strict as Map++import Prediction+import TypeInfo++import System.IO.Unsafe+import System.IO++dot :: a -> a+dot x = unsafePerformIO (putStr "*" >> hFlush stdout >> return x)++epsilon :: Double+epsilon = 0.00001++type CostFunction = TypeEnv -> Size -> FreqMap -> Double++uniform :: CostFunction+uniform env size freqs = chiSquare (fromIntegral size) observed+  where observed = Map.elems (predict env size freqs)++weighted :: [(Name, Int)] -> CostFunction+weighted weights env size freqs = chiSquareVec expected observed+  where+    prediction = predict env size freqs+    (cnames, observed) = unzip (Map.toList (Map.filterWithKey weighted prediction))+    weighted cn cp = isJust (lookup cn weights)+    expected = map multWeight cnames+    multWeight cn = fromIntegral (fromJust (lookup cn weights) * size)+++whitelist :: ([Name] -> TypeEnv -> [Name]) -> [Name] -> CostFunction+whitelist f names env size freqs = chiSquareVec expected observed+  where+    prediction = predict env size freqs+    (cnames, observed) = unzip (Map.toList prediction)+    expected = map applyBan cnames+    applyBan cn+      | cn `elem` f names env = fromIntegral size+      | otherwise = epsilon++blacklist :: ([Name] -> TypeEnv -> [Name]) -> [Name] -> CostFunction+blacklist f names env size freqs = chiSquareVec expected observed+  where+    prediction = predict env size freqs+    (cnames, observed) = unzip (Map.toList prediction)+    expected = map applyBan cnames+    applyBan cn+      | cn `notElem` f names env = fromIntegral size+      | otherwise = epsilon+++types :: [Name] -> TypeEnv -> [Name]+types ts env = filter ((`elem` ts) . typeName . conType env) (consList env)++constructors :: [Name] -> TypeEnv -> [Name]+constructors names _ = names++only, onlyTypes :: [Name] -> CostFunction+only      = whitelist constructors+onlyTypes = whitelist types++without, withoutTypes :: [Name] -> CostFunction+without      = blacklist constructors+withoutTypes = blacklist types++--------------------------------------------------------------------------------++chiSquareVec :: (Floating a) => [a] -> [a] -> a+chiSquareVec expected observed+  = sum (zipWith (\o e -> (o - e)^2 / e) observed expected)++chiSquare :: (Floating a) => a -> [a] -> a+chiSquare expected observed = chiSquareVec (repeat expected) observed+++--+-- Local search optimization method.+--+type Heat = Double++optimizeLS :: TypeEnv -> Size -> CostFunction -> FreqMap -> FreqMap+optimizeLS env size cost freqs+  = localSearch env size (fromIntegral size ^ 2) cost freqs []++localSearch :: TypeEnv -> Size -> Heat -> CostFunction+            -> FreqMap -> [FreqMap] -> FreqMap+localSearch env size heat cost focus visited+  | null newNeighbors = focus+  | delta <= epsilon && heat == 1 = focus+  | delta <= epsilon+    = dot $ localSearch env size 1 cost bestNeighbor newFrontier+  | otherwise+    = dot $ localSearch env size newHeat cost bestNeighbor newFrontier+  where+    delta = focusCost - bestNeighborCost+    focusCost = cost env size focus+    (bestNeighbor, bestNeighborCost) = minimumBy (comparing snd) neighborsCosts+    neighborsCosts = zip newNeighbors (map (cost env size) newNeighbors)+    newNeighbors = neighborhood focus heat \\ (focus:visited)++    newHeat = max 1 ((heat / (1 + 0.01 * (gainRatio / fromIntegral size))))+    gainRatio = bestNeighborCost / focusCost+    newFrontier = newNeighbors ++ (take (length env ^ 2)) visited++neighborhood :: FreqMap -> Heat -> [FreqMap]+neighborhood freqs heat = map (Map.fromList . zip names) neighborFreqs+  where+    (names, ints) = unzip (Map.toList freqs)+    neighborFreqs = concatMap neighborsAt notBuiltInFreqs++    neighborsAt i = [ updateAt i (ints!!i + floor heat)+                    , updateAt i (max 1 (ints!!i - floor heat)) ]+    updateAt i v = take i ints ++ [v] ++ drop (i+1) ints++    notBuiltInFreqs = filter (not . builtIn) (take (length names) [0..])+    builtIn i = Map.member (names !! i) builtInFreqs+
+ src/Prediction.hs view
@@ -0,0 +1,200 @@+{-# LANGUAGE TemplateHaskell #-}++module Prediction where++import Data.Maybe+import Data.Either+import Data.Graph+import Data.List hiding (transpose)+import Data.Matrix hiding ((!), trace)+import Data.Map.Strict (Map, (!))+import qualified Data.Map.Strict as Map++import Test.QuickCheck++import TypeInfo+import Countable++import Debug.Trace++type Size = Int++-- A FreqMap is a mapping between type constructor names and Int values+-- representing the frequencies we want each one of them to occur in a random+-- generated value. This mapping is later provided to QuickCheck `frequency` in+-- order to derivate random value generators.+type FreqMap = Map Name Int++-- Hardcoded instances distributions. This does not work very well, since even+-- if we hardcode the frequencies, the built-in instances does not reduce the+-- size of the inner generation. The solution requires to get rid of the+-- Arbitrary instances and carry arround concrete generators.+builtInFreqs :: FreqMap+builtInFreqs = Map.fromList+  [ {- Bool-}      ('True, 1), ('False, 1)+  , {- Either -}   ('Left, 1), ('Right, 1)+  ]+++initMap :: TypeEnv -> FreqMap+initMap = Map.fromList . map setInitialFreq . consList+  where setInitialFreq cn+          | Map.member cn builtInFreqs = (cn, builtInFreqs ! cn * 100)+          | otherwise = (cn, 100)+++-- A ProbMap is similar to a FreqMap in the sense that it represents types+-- constructor names vs. frequencies. The difference lie in the fact that for+-- every data type T = C1 .. | C2 .. | ... | Cn then it must hold that+-- pC1 + pC2 + ... + pCn = 1.+type ProbMap = Map Name Double+++showMap :: (Show a, Show b) => Map a b -> String+showMap m = intercalate "\n" (map showElem (Map.toList m))+  where showElem e = " * " ++ show e++filterKeys :: (Name -> Bool) -> Map Name b -> Map Name b+filterKeys f = Map.filterWithKey (const . f)++++normalize :: TypeEnv -> FreqMap -> ProbMap+normalize env freqMap = Map.mapWithKey freqRatio freqMap+  where+    freqRatio cn cfreq = fromIntegral cfreq / fromIntegral (freqSum cn)+    freqSum cn = Map.foldr (+) 0 (filterKeys (isSibling env cn) freqMap)+++normalizeTerminals :: TypeEnv -> FreqMap -> ProbMap+normalizeTerminals env freqMap = Map.mapWithKey freqRatio terminalsMap+  where+    terminalsMap = filterKeys (isTerminal env) freqMap+    freqRatio cn cfreq = fromIntegral cfreq / fromIntegral (freqSum cn)+    freqSum cn = Map.foldr (+) 0 (filterKeys (isSibling env cn) terminalsMap)+++genGWMatrix :: TypeEnv -> ProbMap -> Matrix Double+genGWMatrix env probMap = matrix size size genElem+  where+    size = length env+    genElem (m, n) = sum $ map multProb $ occsFromTo (env!!(m-1)) (env!!(n-1))+    multProb (cn, occs) = probMap ! cn * fromIntegral occs+    occsFromTo from to = map (conOccurrences to) (tcons from)+    conOccurrences to con = (cname con, occurrences (tsig to) con)+++-- Predicts the distribution for a given type constructor frequencies map.+predict :: TypeEnv -> Size -> FreqMap -> ProbMap+predict env size freqs = prediction+  where++    -- Normalize the frequencies into probabilities+    allProbs  = normalize env freqs+    termProbs = normalizeTerminals env freqs++    -- Split the type environment into branching types and leaf types.+    rootType = env !! 0+    isBranchingType t = t == rootType || any rec (tcons t)+    (branchingTypes, leafTypes) = partition isBranchingType env++    -- Helpers+    bct = tsig . conType branchingTypes     -- branching constructor type+    lct = tsig . conType leafTypes          -- simple constructor type+    m !$ cn = Map.findWithDefault 0 cn m    -- safe lookup++    ----------------------------------------------------------------------------+    -- First we need to calculate the expectancy of the types involved at the+    -- branching process, we do this by calculating the pure random generation+    -- process at the first (size-1) levels, and adding the expectancy of the+    -- pseudo-random generation of the last level.+    ----------------------------------------------------------------------------+    branchingTypesExp = Map.unionWith (+) brFirstLevels brLastLevel++    branchingProbs = filterKeys isBranchingTypeCon allProbs+    branchingTermProbs = filterKeys isBranchingTypeCon termProbs++    isBranchingTypeCon cn = cn `elem` consList branchingTypes+    branchingSigs = typeSigs branchingTypes++    -- Generate the Galton-Watson matrix with the given branching probabilities.+    mT  = genGWMatrix branchingTypes branchingProbs+    ez0 = fromList 1 (length branchingTypes) (1 : repeat 0)++    genLevel 0 = ez0+    genLevel k = ez0 * (mT^k)++    {- Branching process @ first (size-1) levels -}+    brFirstLevels = Map.mapWithKey multTypeExp branchingProbs+      where+        multTypeExp cn cp+          -- Is safe to use the geometric series simplification formula+          | length branchingTypes == 1 && mT' /= 1+            = cp * ((1 - mT' ^ size) / (1 - mT'))+          -- Otherwise, we need to sum every level :(+          | otherwise = cp * typeExp ! bct cn++        mT' = getElem 1 1 mT+        typeExp = Map.fromList $ zip branchingSigs (toList predMatrix)+        predMatrix = foldr1 (+) (map genLevel [0..size-1])+++    {- Branching process @ last level -}+    brLastLevel = Map.mapWithKey sumTermExp branchingTermProbs+      where+        sumTermExp tn tp+          = sum [ tp+                * allProbs ! cname con+                * fromIntegral (occurrences (bct tn) con)+                * prevLvlExp ! bct (cname con)+                | con <- concatMap tcons branchingTypes ]++        prevLvlExp = Map.fromList $ zip branchingSigs (toList (genLevel (size-1)))+++    ----------------------------------------------------------------------------+    -- Once we have the expectancy for every type constructor involved at the+    -- branching process, we can incorporate the expectancy of the leaf types by+    -- counting how many times they are generated as result of the branching+    -- process. It is important to note here that a leaf type could generate+    -- another leaf type, so we need to perform a topological sort in order to+    -- start calculating the expectancy of the 'nearest' types to the branching+    -- process ones. This way the farthest ones are not multiplied by zero.+    ----------------------------------------------------------------------------+    prediction = addLeafTypesExp branchingTypesExp sortedLeafTypeCons++    addLeafTypesExp pred [] = pred+    addLeafTypesExp pred (cn:cns)+      = addLeafTypesExp (Map.insert cn (sumOccurrences pred cn) pred) cns++    sumOccurrences pred cn+      = sum [ allProbs ! cn+            * pred !$ cname con+            * fromIntegral (occurrences (lct cn) con)+            | con <- allCons ]++    allCons = concatMap tcons env+    leafTypeCons = concatMap tcons leafTypes++    generatorsOf cn = [ cname con | con <- allCons, any (== lct cn) (cargs con) ]++    sortedLeafTypeCons = reverse (map (extractCName . gvert) (topSort graph))+    (graph, gvert) = graphFromEdges' leafTypeDeps+    leafTypeDeps = map createVertex leafTypeCons+    extractCName (_, cn, _) = cn+    createVertex con = ((), cname con, generatorsOf (cname con))++++-- Generates a bunch of samples using a given generator and prints the average+-- number of type constructors generated in a random sample of size n. The+-- _arb_ parameter needs a type annotation when the function is used with+-- _arbitraty_ in order to break the ambiguity.+-- E.g.  confirm 10 (arbitrary @Tree)+confirm :: (Countable a) => Size -> Gen a -> IO ()+confirm size arb = do+  let samples = 100000+  values <- sequence (replicate samples (generate (resize size arb)))+  let consCount = Map.unionsWith (+) (map count values)+      consAvg = Map.map (\c -> fromIntegral c / fromIntegral samples) consCount+  putStrLn (showMap consAvg)
+ src/Reification.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE BangPatterns #-}++module Reification where++import Data.List+import Control.Monad.Extra++import Language.Haskell.TH hiding (Type, Con, prim)+import qualified Language.Haskell.TH as TH++import Megadeth+import TypeInfo+++-- Add some info to unsupported features error messages.+unsupported :: Show value => String -> value -> a+unsupported fun input = error $ fun ++ ": unsupported input: " ++ show input++-- Given a type name, extracts its information and the information of the types+-- involved with it from the compiling environment. This functions performs a+-- reachability analysis, looking for mutually recursive type definitions, and+-- marks the _rec_ field of each type constructor accordingly.+-- IMPORTANT: This function should be called with non-parametric (kind ~ *)+-- type names, since we can not resolve the implicit type vars. To reify fully+-- instantiated parametric types, first define a non-parametric type synonym of+-- the target (e.g. type MaybeInt = Maybe Int), or use _reifyTypeEnv_ instead.+reifyNameEnv :: Name -> Q TypeEnv+reifyNameEnv = reifyName >=> reifyInvolvedTypes++-- Similar to _reifyTypeEnv_, but allows us to reify parametric type+-- definitions without needing to define an ad-hoc non-parametric type synonym.+-- E.g. reifyNameEnv ''MaybeInt == reifyTypeEnv (Base ''Maybe `App` Base ''Int)+reifyTypeEnv :: Type -> Q TypeEnv+reifyTypeEnv = reifyType >=> reifyInvolvedTypes++++-- Given a type name, extracts its information from the compiling environment.+-- Note this function does not mark the type constructors as mutually recursive+-- if they are mutually recursive with any other type.+-- IMPORTANT: This function should be called with non-parametric (kind ~ *)+--            type names, since we can not resolve the implicit type vars.+reifyName :: Name -> Q TypeDef+reifyName name = reifyType (Base name)+++-- Given a type, reifies the leftmost type constructor, and instantiates its+-- type vars with the types applied to the original type.+-- E.g.  reifyType (Base ''Maybe `App` Base ''Int)+--       ===>+--       TypeDef { tsig = App (Base ''Maybe) (Base ''Int)+--               , tcons = [ Con { cname = 'Nothing, ... }+--                         , Con { cname = 'Just, cargs = [Base ''Int], ...} ]+--               , ... }+reifyType :: Type -> Q TypeDef+reifyType this = do++  let (name, args) = unapply this+  reify name >>= \case++    {- data T {...} = C1 {...} | C2 {...} | ... -}+    TyConI (DataD _ _ vars _ cons _) -> do+      let vars' = map extractTVar vars+          binds = zip vars' args+          cons' = map (extractCon binds this) cons+          fields = concatMap (concatMap flatten . cargs) cons'+          emptyCon = Con { cname = name, cargs = [], rec = False }++      primitive <- any isPrim <$> mapM reify fields++      if primitive+      then return+        TypeDef { tsig = apply name [], tcons = [emptyCon], prim = True }+      else return+        TypeDef { tsig = this, tcons = cons', prim = False }++    {- newtype T {...} = Con T' -}+    TyConI (NewtypeD _ _ vars _ con _ ) -> do+      let vars' = map extractTVar vars+          binds = zip vars' args+          con' = extractCon binds this con+          fields = concatMap flatten (cargs con')+          emptyCon = Con { cname = name, cargs = [], rec = False }++      primitive <- any isPrim <$> mapM reify fields++      if primitive+      then return+        TypeDef { tsig = apply name [], tcons = [emptyCon], prim = True }+      else return+        TypeDef { tsig = this, tcons = [con'], prim = False }++    {- type T {...} = U {...} -}+    TyConI (TySynD _ vars ty) -> do+      let vars' = map extractTVar vars+          binds = zip vars' args+          realT = instantiate binds (extractType ty)+      realDef <- reifyType realT+      return realDef { tsig = this }++    {- Int#, Bool#, ...  -}+    PrimTyConI {} -> return+        TypeDef { tsig = apply name [], tcons = [], prim = True }++    {- Not supported yet. -}+    x -> unsupported "reifyType" x+++extractTVar :: TyVarBndr -> Name+extractTVar (PlainTV tv) = tv+extractTVar (KindedTV tv _) = tv++extractCon :: [(Name, Type)] -> Type -> TH.Con -> Con+extractCon binds this (NormalC cn cas)+  = Con { cname = cn, cargs = args, rec = this `elem` args }+  where args = map (instantiate binds . extractType . snd) cas+extractCon binds this (InfixC lt op rt)+  = Con { cname = op, cargs = args, rec = this `elem` args }+  where args = map (instantiate binds . extractType . snd) [lt,rt]+extractCon binds this (RecC cn vbts)+  = Con { cname = cn, cargs = args, rec = this `elem` args }+  where args = map (instantiate binds . extractType . (\(_,_,x) -> x)) vbts+extractCon _ _ x = unsupported "extractCon" x++extractType :: TH.Type -> Type+extractType (AppT t1 t2) = App (extractType t1) (extractType t2)+extractType (ConT nm) = Base nm+extractType (VarT nm) = Var nm+extractType (TupleT s) = Base (TH.tupleTypeName s)+extractType ListT = Base ''[]+extractType x = unsupported "extractType" x++instantiate :: [(Name, Type)] -> Type -> Type+instantiate binds (Base name) = Base name+instantiate binds (App l r) = App (instantiate binds l) (instantiate binds r)+instantiate binds (Var v) = maybe (Var v) id (lookup v binds)++++-- Traverse a data type definition, extracting recursively every data type+-- reachable from the root data type. This function also calculates mutually+-- recursive dependencies in type constructors and updates its `rec` field+-- accordingly.+reifyInvolvedTypes :: TypeDef -> Q TypeEnv+reifyInvolvedTypes root = addMutRecLoops <$> reifyInvolvedTypes' [root] root+  where+    reifyInvolvedTypes' _ this | prim this = return [this]+    reifyInvolvedTypes' visited this = do+        let newTypes = involvedWith this \\ map tsig visited+        newTypeDefs <- mapM reifyType newTypes+        newReached <- mapM (reifyInvolvedTypes' (this:visited)) newTypeDefs+        return (nub (this : concat newReached))+++-- Calculate if any type constructor is mutually recursive and update the+-- `rec` field accordingly.+addMutRecLoops :: TypeEnv -> TypeEnv+addMutRecLoops env = map (addMutRecLoop env) env+  where+    addMutRecLoop env this+      = this { tcons = map (setIsRecursive env (tsig this)) (tcons this) }++    setIsRecursive env this con+        | rec con = con+        | reachableFrom env this con = con { rec = True }+        | otherwise = con++    reachableFrom env this con = any (reachableFrom' env this []) (cargs con)++    reachableFrom' env this visited arg+        | this `subtype` arg  = True+        | any (this `subtype`) argImmDefs = True+        | otherwise = any (reachableFrom' env this (arg:visited)) nextArgs+          where+            argDef = find ((==arg) . tsig) env+            argImmDefs = maybe [] involvedWith argDef+            nextArgs = maybe [] (\def -> involvedWith def \\ visited) argDef+            -- ToDo: look why some definitions are not in the env sometimes.+            -- Just nextArgs = involvedWith argDef \\ visited
+ src/TypeInfo.hs view
@@ -0,0 +1,116 @@+module TypeInfo+( module Language.Haskell.TH+, module TypeInfo+) where++import Data.List+import Data.Maybe+import Data.Function++import qualified Language.Haskell.TH as TH+import Language.Haskell.TH (Name)++import Debug.Trace++-- Data types for representing Haskell data type definitions.+data TypeDef = TypeDef+    { tsig :: Type      -- ^ Left hand side of the type definition.+    , tcons :: [Con]    -- ^ Type constructors+    , prim :: Bool      -- ^ Is this data type primitive? (e.g. Int, Bool)+    } deriving Show++data Con = Con+    { cname :: Name     -- ^ Constructor name+    , cargs :: [Type]   -- ^ Constructor args+    , rec :: Bool       -- ^ Is this constructor recursive?+    } deriving Show++data Type+    = Base Name         -- ^ A base type name (e.g. Int, Maybe, Either)+    | Var Name          -- ^ A type variable (e.g. a, b)+    | App Type Type     -- ^ A type application (e.g. T U)+    deriving (Show, Eq, Ord)++instance Eq TypeDef where+    (==) = (==) `on` tsig++apply :: Name -> [Type] -> Type+apply name = foldl App (Base name)++unapply :: Type -> (Name, [Type])+unapply (Base name) = (name, [])+unapply (Var name) = (name, [])+unapply (App l r) = (name, l' ++ [r])+  where (name, l') = unapply l++typeName :: TypeDef -> Name+typeName = fst . unapply . tsig++typeArgs :: TypeDef -> [Type]+typeArgs = snd . unapply . tsig++flatten :: Type -> [Name]+flatten (Base name) = [name]+flatten (Var name) = [name]+flatten (App l r) = flatten l ++ flatten r++subtype :: Type -> Type -> Bool+subtype t t' | t == t' = True+subtype t (App l r) = subtype t l || subtype t r+subtype t t' = False++occurrences :: Type -> Con -> Int +occurrences ts con = countSat (==ts) (cargs con)++countSat :: (a -> Bool) -> [a] -> Int+countSat p = length . filter p++++type TypeEnv = [TypeDef]++-- Extract type signatures from a type env.+typeSigs :: TypeEnv -> [Type]+typeSigs = map tsig++-- Extract the list of type constructor names from a type env.+consList :: TypeEnv -> [Name]+consList env = nub (map cname (concatMap tcons env))++-- Extracts type parameters of every type constructor.+involvedWith :: TypeDef -> [Type]+involvedWith = nub . concatMap cargs . tcons++-- Extracts a type constructor from a given type.+getCon :: Name -> TypeDef -> Con+getCon cn t = fromMaybe+    (error $ "getCon: looking for " ++ show cn ++ " in " ++ show (tsig t))+    (find ((cn==) . cname) (tcons t))++-- Given a type constructor name, finds its associated type.+conType :: TypeEnv -> Name -> TypeDef+conType env cn = fromMaybe+    (error $ "conType: " ++ show cn ++ " not found in " ++ show env)+    (find (any ((cn==) . cname) . tcons) env)++-- Given a type constructor name, returns its siblings (including itself).+getSiblings :: Name -> TypeEnv -> [Con]+getSiblings cn env = tcons (conType env cn)++-- Is a type constructor sibling with another?+isSibling :: TypeEnv -> Name -> Name -> Bool+isSibling env = (==) `on` conType env++-- Split constructors into recursive and terminals.+splitCons :: TypeEnv -> ([Con], [Con])+splitCons = partition rec . concatMap tcons++getRecursives :: TypeEnv -> [Con]+getRecursives = fst . splitCons++getTerminals :: TypeEnv -> [Con]+getTerminals = snd . splitCons++-- Is this constructor terminal?+isTerminal :: TypeEnv -> Name -> Bool+isTerminal env cn = cn `elem` map cname (getTerminals env)
+ test/Examples.hs view
@@ -0,0 +1,190 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveGeneric  #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE FlexibleInstances #-}++module Examples where++import Test.QuickCheck+import Language.Haskell.TH+import GHC.Generics++import Countable+import Dragen++deriving instance Generic Int+instance Countable Int+instance Countable a => Countable (Maybe a)++--------------------------------------------------------------------------------++--+-- Binary tree with three kinds of leafs+--++data Tree+  = LeafA+  | LeafB+  | LeafC+  | Node Tree Tree+  deriving (Show, Generic)++-- Countable generic typeclass let us count the number of each kind of+-- constructors are present within a value. We automatically derive a type+-- instance for Tree as follows:+instance Countable Tree++-- Now we derive and optimize a random generator for Tree, defining an Arbitrary+-- Tree class instance. The predicted distribution of the derived generator is+-- shown in the derivation process as follows:++dragenArbitrary ''Tree 10 uniform++-- Finally, we can confirm the predicted distribution of this derived generator+-- sampling a big number of values, and averaging the number of generated+-- constructors of each kind of type constructor:+confirmTree :: IO ()+confirmTree = confirm 10 (arbitrary :: Gen Tree)+-- =====>+--  * ("LeafA",5.23938)+--  * ("LeafB",5.23396)+--  * ("LeafC",5.18283)+--  * ("Node",14.65617)++--------------------------------------------------------------------------------++--+-- Mutually recursive data types+--++data T1+  = A+  | B T1 T2+  deriving (Show, Generic)++data T2+  = C+  | D T1+  deriving (Show, Generic)++instance Countable T1+instance Countable T2++dragenArbitrary ''T1 10 uniform++confirmT1 = confirm 10 (arbitrary :: Gen T1)+-- =====>+--  * ("A",8.63757)+--  * ("B",13.67604)+--  * ("C",6.03847)+--  * ("D",7.63757)++--------------------------------------------------------------------------------++--+-- Rose trees (mutually recursive between Rose and [] data types) with composite+-- types Maybe and Bool+--++data Rose+  = RLeaf (Maybe Bool)+  | RNode [Rose]+  deriving (Show, Generic)++instance Countable a => Countable [a]+instance Countable Rose+instance Countable Bool++dragenArbitrary ''Rose 10 uniform++confirmRose = confirm 10 (arbitrary :: Gen Rose)+-- =====>+--  * (":",16.61068)+--  * ("False",4.17641)+--  * ("Just",8.35818)+--  * ("Nothing",2.79404)+--  * ("RLeaf",11.15222)+--  * ("RNode",6.45846)+--  * ("True",4.18177)+--  * ("[]",6.45846)++--------------------------------------------------------------------------------++--+-- Weighted generation.+--++data Tree'+  = Leaf+  | NodeA Tree' Tree'+  | NodeB Tree' Tree'+  deriving (Show, Generic)++instance Countable Tree'++dragenArbitrary ''Tree' 10 (weighted [('NodeA, 3), ('NodeB, 1)])++confirmTree' = confirm 10 (arbitrary :: Gen Tree')+-- =====>+--  * ("Leaf",41.11079)+--  * ("NodeA",30.47836)+--  * ("NodeB",9.63243)++--------------------------------------------------------------------------------++--+-- Lambda expressions with weighted generation+--++data Expr+  = Var Char+  | App Expr Expr+  | Lam Char Expr+  deriving (Show, Generic)++deriving instance Generic Char+instance Countable Char+instance Countable Expr++dragenArbitrary ''Expr 10 (weighted [('Var, 3), ('Lam, 1)])++confirmExpr = confirm 10 (arbitrary :: Gen Expr)+-- =====>+--  * ("App",29.15031)+--  * ("C#",40.21184)   (boxed char)+--  * ("Lam",10.06153)+--  * ("Var",30.15031)++--------------------------------------------------------------------------------++--+-- Lisp expressions used in the paper presentation+--++data Text +  = Text+  deriving (Show, Generic)++data Lisp+  = Symbol Text+  | String Text+  | Number Int+  | List [Lisp]+  deriving (Show, Generic)++instance Countable Text+instance Countable Lisp++dragenArbitrary ''Lisp 10 uniform++confirmLisp = confirm 10 (arbitrary :: Gen Lisp)+-- =====>+-- * (GHC.Types.:,17.838586498464217)+-- * (GHC.Types.[],7.0449940565497196)+-- * (Examples.List,7.0449940565497196)+-- * (Examples.Number,5.018549975282765)+-- * (Examples.String,3.3875212333158666)+-- * (Examples.Symbol,3.3875212333158666)+-- * (Examples.Text,6.775042466631733)+-- * (GHC.Types.Int,5.018549975282765)+
+ test/Main.hs view
@@ -0,0 +1,6 @@+module Main where++import Examples+import Dragen++main = putStrLn "\n\nExamples were compiled correctly!"
+ test/TestCountable.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE DeriveGeneric  #-}+{-# LANGUAGE StandaloneDeriving #-}++module TestCountable where++import GHC.Generics+import Countable++----------------------------------------++data T1+    = A+    | B T1 T2+    deriving (Show, Generic)++data T2+    = C+    | D T1+    deriving (Show, Generic)++instance Countable T1+instance Countable T2++countT1T2 = count (B (B (B A (D (B A C))) (D A)) (D A))+-- ==> fromList [("A",4),("B",4),("C",1),("D",3)]++----------------------------------------++data List+    = Nil+    | Cons () List+    deriving (Show, Generic)++instance Countable List+instance Countable ()++countList = count (Cons () (Cons () Nil))+-- ==> fromList [("()",2),("Cons",2),("Nil",1)]++----------------------------------------++data Tree+    = Leaf+    | Node Tree Tree+    deriving (Show, Generic)++instance Countable Tree++countTree = count (Node (Node Leaf Leaf) (Node Leaf Leaf))+-- ==> fromList [("Leaf",4),("Node",3)]++----------------------------------------++data GTree a+    = GLeaf+    | GNode (GTree a) a (GTree a)+    deriving (Show, Generic1)++deriving instance (Generic a) => Generic (GTree a)+instance (Generic a, Countable a) => Countable (GTree a)++instance Countable Bool++countGTree = count (GNode (GNode GLeaf True GLeaf) False (GNode GLeaf True GLeaf))+-- ==> fromList [("False",1),("GLeaf",4),("GNode",3),("True",2)]++instance Countable1 GTree++count1GTreeInt = count1 (GNode (GNode GLeaf 1    GLeaf) 2     (GNode GLeaf 3    GLeaf))+-- ==> fromList [("GLeaf",4),("GNode",3)]++count1GTreeBool = count (GNode (GNode GLeaf True GLeaf) False (GNode GLeaf True GLeaf))+-- ==> fromList [("GLeaf",4),("GNode",3)]