packages feed

catamorphism (empty) → 0.3.0.0

raw patch · 4 files changed

+230/−0 lines, 4 filesdep +basedep +template-haskellsetup-changed

Dependencies added: base, template-haskell

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Frerich Raabe++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 Frerich Raabe nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ catamorphism.cabal view
@@ -0,0 +1,26 @@+name:                catamorphism+version:             0.3.0.0+synopsis:            A package exposing a helper function for generating catamorphisms.+description:         A package exposing a helper function for generating catamorphisms.+homepage:            http://github.com/frerich/catamorphism+license:             BSD3+license-file:        LICENSE+author:              Frerich Raabe+maintainer:          frerich.raabe@gmail.com+bug-reports:         https://github.com/frerich/makemorphism/issues+copyright:           Copyright (c) 2014, 2015 Frerich Raabe <frerich.raabe@gmail.com>+category:            Development+build-type:          Simple+cabal-version:       >=1.10+stability:           experimental++source-repository head+  type:       git+  location:   http://github.com/frerich/catamorphism++library+  exposed-modules:     Data.Morphism.Cata+  build-depends:       base >=4.6 && <4.8, template-haskell >=2.8 && <2.10+  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -Wall
+ src/Data/Morphism/Cata.hs view
@@ -0,0 +1,172 @@+{-|+Module:         Data.Morphism.Cata+Copyright:      (c) 2014 Frerich Raabe+License:        BSD3+Maintainer:     frerich.raabe@gmail.com+Stability:      experimental++This module exposes a 'makeCata' function which can create catamorphisms for+arbitrary Haskell types. Catamorphisms are functions which deconstruct some+value by replacing each data constructor with a custom function yielding a new+value. See <http://www.haskell.org/haskellwiki/Catamorphisms> for a more+in-depth discussion of catamorphisms in Haskell.++The Haskell base package already comes with a couple of standard catamorphisms+such as 'bool' (for Bool values), 'maybe' (for Maybe values) values, 'either' for (Either values)+values and foldr (for lists). These catamorphisms could have been generated using+'makeCata' as follows:++> -- Defines 'bool :: a -> a -> Bool -> a'+> $(makeCata defaultOptions ''Bool)+>+> -- Defines 'maybe :: b -> (Maybe a -> b) -> Maybe a -> b'+> $(makeCata defaultOptions ''Maybe)+>+> -- Defines 'either :: (a -> c) -> (b -> c) -> Either a b -> c'+> $(makeCata defaultOptions ''Either)++However, catamorphisms are especially useful for recursive data structures. Consider+the following simple example which defines a basic data type for modelling sums+of numbers, supporting variables:++> {-# LANGUAGE TemplateHaskell #-}+> +> import Data.Morphism.Cata+> import Data.Maybe (fromJust)+> import Data.Function (on)+> +> data Expr a = Number a+>             | Variable Char+>             | Sum (Expr a) (Expr a)+> +> $(makeCata defaultOptions { cataName = "cataExpr" } ''Expr)++The 'makeCata' invocation defines a 'cataExpr' function which works like a fold on+'Expr' values; it can be used to implement various useful other functions:++> -- Evaluate an Expr, given some variable bindings+> eval :: Num a => [(Char, a)] -> Expr a -> a+> eval vars = cataExpr id (fromJust . (`lookup` vars)) ((+) `on` eval vars)+> +> -- Pretty-prints an Expr+> pprint :: Show a => Expr a -> String+> pprint = cataExpr show show (\a b -> pprint a ++ " + " ++ pprint b)+> +> -- Counts the number of variables used in an expr+> numVars :: Expr a -> Int+> numVars = cataExpr (const 1) (const 0) ((+) `on` numVars)+-}++module Data.Morphism.Cata+    ( CataOptions(..)+    , defaultOptions+    , makeCata+    )+where++import Control.Monad (forM, replicateM)++import Data.Char (toLower)+import Data.Functor ((<$>))++import Language.Haskell.TH++{-|+    Values of the 'CataOptions' type can be passed to 'makeCata' in order to+    customize the generated catamorphism. At this point, only the name of the+    function can be changed.+-}+data CataOptions = CataOptions {+    {-|+        The desired name for the catamorphism. An empty string will make+        'makeCata' derive the catamorphism name from the type by just taking+        the type name and making the first letter lower-case.+    -}+    cataName :: String+}++{-|+    The default catamorphism generation options; the catamorphism will be named+    after the type, e.g.++    > $(makeCata defaultOptions ''Bool)++    defines a function 'bool'.+-}+defaultOptions :: CataOptions+defaultOptions = CataOptions ""++makeFuncT :: Type -> Type -> Type+makeFuncT a = AppT (AppT ArrowT a)++conArgTypes :: Con -> [Type]+conArgTypes (NormalC _ args)     = map snd args+conArgTypes (RecC _ args)        = map (\(_,_,x) -> x) args+conArgTypes (InfixC arg1 _ arg2) = map snd [arg1, arg2]+conArgTypes (ForallC _ _ c)      = conArgTypes c++conName :: Con -> Name+conName (NormalC n _)   = n+conName (RecC n _)      = n+conName (InfixC _ n _)  = n+conName (ForallC _ _ c) = conName c++conType :: Name -> Con -> Type+conType resultT c = foldr makeFuncT (VarT resultT) (conArgTypes c)++-- |The 'makeCata' function creates a catamorphism for the given type.+makeCata :: CataOptions     -- Options to customize the catamorphism; the name of the defined function can be changed+         -> Name            -- The type to generate a catamorphism for.+         -> Q [Dec]+makeCata opts typeName = do+    typeInfo <- reify typeName+    (tyVarBndrs, cons) <- case typeInfo of+            TyConI (DataD _ _ tyVarBndrs cons _)   -> return (tyVarBndrs, cons)+            TyConI (NewtypeD _ _ tyVarBndrs con _) -> return (tyVarBndrs, [con])+            _                                      -> fail "makeCata: Expected name of type constructor"+    sequence [signature tyVarBndrs cons, funDef cons]+  where+    signature :: [TyVarBndr] -> [Con] -> Q Dec+    signature tyVarBndrs cons = do+        let tyVarNames = map tyVarName tyVarBndrs+        let typeConType = foldl AppT (ConT typeName) (map VarT tyVarNames)+        resultTypeName <- newName "a"+        let args = map (conType resultTypeName) cons ++ [typeConType, VarT resultTypeName]+        return (SigD funName (ForallT (PlainTV resultTypeName : tyVarBndrs) [] (foldr1 makeFuncT args)))++    funDef :: [Con] -> Q Dec+    funDef cons = (FunD funName . (:[])) <$> funImpl cons++    funName :: Name+    funName = mkName $+        if null (cataName opts)+            then let (x:xs) = nameBase typeName in toLower x : xs+            else cataName opts++    funImpl :: [Con] -> Q Clause+    funImpl cons = do+        conArgs <- replicateM (length cons) (VarP <$> newName "c")++        valueArgName <- newName "x"+        let funArgs = conArgs ++ [VarP valueArgName]++        matches <- forM (zip cons conArgs) $ \(c, VarP cn) -> do+            pat@(ConP _ conPats) <- conToConP c+            let patNames = map (\(VarP n) -> n) conPats++            let bodyE = foldl1 AppE . map VarE $ (cn : patNames)+            return (Match pat (NormalB bodyE) [])++        let bodyE = CaseE (VarE valueArgName) matches+        return (Clause funArgs (NormalB bodyE) [])++      where+        conToConP :: Con -> Q Pat+        conToConP c = do+            argNames <- replicateM (length . conArgTypes $ c) (VarP <$> newName "a")+            return (ConP (conName c) argNames)++tyVarName :: TyVarBndr -> Name+tyVarName (PlainTV n)    = n+tyVarName (KindedTV n _) = n+