packages feed

functor-friends (empty) → 0.1.0.0

raw patch · 8 files changed

+344/−0 lines, 8 filesdep +basedep +recursion-schemessetup-changed

Dependencies added: base, recursion-schemes

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Matt Noonan (c) 2017++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 Matt Noonan 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,1 @@+# functor-friends
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ functor-friends.cabal view
@@ -0,0 +1,29 @@+name:                functor-friends+version:             0.1.0.0+synopsis:            Friendly helpers for your recursion schemes.+description:         A library to assist with manipulating and modifying+                     types defined by fixpoints.+homepage:            https://github.com/matt-noonan/functor-friends#readme+license:             BSD3+license-file:        LICENSE+author:              Matt Noonan+maintainer:          matt.noonan@gmail.com+copyright:           (c) 2017 Matt Noonan+category:            Data Structures+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Data.Functor.Holey+                     , Data.Functor.Annotated+                     , Data.Functor.Decomposed+                     , Data.Functor.Tutorial+  build-depends:       base >= 4.7 && < 5+                     , recursion-schemes+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/matt-noonan/functor-friends
+ src/Data/Functor/Annotated.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE DeriveFunctor, TypeFamilies, PatternSynonyms, RankNTypes #-}++module Data.Functor.Annotated+  ( -- * The @Annotated@ type family+    Annotated+    +    -- * Types+  , AnnotatedF(..)+    +    -- ** Patterns for hiding @Fix@+  , pattern Note++    -- * Utility functions+  , note+  , erase+  , foldMemo+    +  ) where++import Data.Functor.Foldable (Fix(..), fold, unfold)+import Data.Functor.Decomposed++-- | @AnnotatedF a f@ transforms the functor @f@ to add+-- @a@-valued annotations.++data AnnotatedF a f t+  = NoteF { note_  :: a+          , erase_ :: f t+          }+  deriving (Eq, Ord, Functor)++pattern Note a e = Fix (NoteF a e)++instance Decomposed (AnnotatedF a) where+  fmap1 f n = n { erase_ = f (erase_ n) }+  +-- | If @T@ is a type that is defined as a fixpoint, then+-- an @Annotated a T@ will be the type of "@T@s with an+-- @a@-valued annotation attached to each node".++type family   Annotated a f+type instance Annotated a (Fix f) = Fix (AnnotatedF a f)++-- | Extract the top-level annotation.++note :: Fix (AnnotatedF t f) -> t+note (Note t _) = t++-- | Discard the annotations.++erase :: Functor f => Fix (AnnotatedF t f) -> Fix f+erase = unfold (\(Note _ e) -> e)++-- | Fold a function over a data structure, storing the+-- sub-results as annotations.++foldMemo :: Functor f => (f t -> t) -> Fix f -> Fix (AnnotatedF t f)+foldMemo phi = fold phi'+  where+    phi' x = Note (phi $ fmap note x) x
+ src/Data/Functor/Decomposed.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE RankNTypes #-}++module Data.Functor.Decomposed+  ( Decomposed(..)+  , natmap+  ) where++import Data.Functor.Foldable (Fix(..), unfix, unfold)++-- | A type class for types @d :: (* -> *) -> * -> *@ that+-- represent compositions of functors. For a functor @f :: * -> *@,+-- @d f t@ should look like some functor applied to the type @f t@,+-- and @fmap1@ is just @fmap@ for that functor.+class Decomposed d where+  fmap1 :: (f t -> g t) -> d f t -> d g t++-- | Given a natural transformation between functors, produce a+-- function between the fixpoints of those functors.+natmap :: (Functor f, Functor g)+       => (forall t. f t -> g t)+       -> Fix f+       -> Fix g+natmap f = unfold (f . unfix)
+ src/Data/Functor/Holey.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE DeriveFunctor, TypeFamilies, PatternSynonyms, RankNTypes #-}++module Data.Functor.Holey+  ( -- * The @Holey@ type family+    Holey+    +    -- * Types+  , HoleF(..)+    +    -- ** Patterns for hiding @Fix@+  , pattern Hole+  , pattern Existing++    -- * Utility functions+  , whole+  , plug+  , punch+    +  ) where++import Data.Functor.Foldable (Fix(..), unfix, fold, unfold)+import Data.Functor.Decomposed++-- | @HoleF f@ transforms the functor @f@ to add "holes".++data HoleF f t+  = HoleF           -- ^ A hole.+  | ExistingF (f t) -- ^ A normal value.+  deriving (Eq, Ord, Functor)++pattern Hole = Fix HoleF+pattern Existing e = Fix (ExistingF e)++instance Decomposed HoleF where+  fmap1 _ HoleF = HoleF+  fmap1 f (ExistingF e) = ExistingF (f e)++-- | If @T@ is a type that is defined as a fixpoint, then+-- a @Holey T@ will be the type of "@T@s with holes".++type family   Holey f+type instance Holey (Fix f) = Fix (HoleF f)++-- | Upgrade a value of type @T@ to a value of type+-- @T@-with-holes, without actually introducing any holes.++whole :: Functor f => Fix f -> Fix (HoleF f)+whole = unfold (ExistingF . unfix)++-- | Fill all holes in a data structure with the given value.++plug :: Functor f => Fix f -> Fix (HoleF f) -> Fix f+plug x = fold phi+  where+    phi HoleF = x+    phi (ExistingF e) = Fix e+  +-- | Replace any substructure matching the predicate with a hole.++punch :: Functor f => (Fix f -> Bool) -> Fix f -> Fix (HoleF f)+punch test = unfold phi+  where+    phi x@(Fix e) = if test x then HoleF else ExistingF e
+ src/Data/Functor/Tutorial.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE LambdaCase, PatternSynonyms, DeriveFunctor #-}+{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}++module Data.Functor.Tutorial where++import Data.Functor.Holey+import Data.Functor.Annotated+import Data.Functor.Decomposed++import Data.Functor.Foldable ( Fix(..), fold, cata )++-- | An example AST type, as an F-algebra. This AST will be used+-- to demonstrate some tricks for composing new data types+-- while maintaining separation of concerns.+--+-- This approach was inspired by the @recursion-schemes@ library+-- and the "Data Types a la Carte" paper.++data AstF t+  = RealF    Double+  | ComplexF Double Double+  | VarF String+  | AddF t t+  | MulF t t+  | AbsF t+  deriving (Eq, Ord, Functor)++type Ast = Fix AstF++-- | Patterns to simplify the construction and destructuring of ASTs+pattern Add x y = Fix (AddF x y)+pattern Mul x y = Fix (MulF x y)+pattern Abs x   = Fix (AbsF x)+pattern Real x  = Fix (RealF x)+pattern Complex x y = Fix (ComplexF x y)+pattern Var x   = Fix (VarF x)++-- | A pretty-printing typeclass.+--+-- This demonstrates an approach to separation of concerns; the+-- pretty-printer for normal ASTs is implemented independently from+-- the pretty-printer for holes and the pretty-printer for annotations.+--+-- Although the implementations don't know about each other, they can+-- still be composed; as a result, a @Holey Ast@ knows how to print itself+-- using a mixture of the pretty-printer for @Ast@s and the pretty-printer+-- for @Hole@s.++class Pretty a where+  pretty :: a -> String++-- | Pretty-printer for fixpoints.+instance Pretty (f (Fix f)) => Pretty (Fix f) where+  pretty (Fix x) = pretty x++-- | Pretty-printer for ASTs.+instance Pretty t => Pretty (AstF t) where+  +  pretty = \case+        RealF d        -> show d+        ComplexF re im -> paren (show re ++ " + " ++ show im ++ "i")+        AddF x y       -> paren (pretty x ++ " + " ++ pretty y)+        MulF x y       -> paren (pretty x ++ " * " ++ pretty y)+        AbsF x         -> "|" ++ pretty x ++ "|"+        VarF v         -> v+    where+      paren x = "(" ++ x ++ ")"++-- | Pretty-printer for holes.+instance Pretty (f (Fix (HoleF f))) => Pretty (HoleF f (Fix (HoleF f))) where++  pretty HoleF         = "_"+  pretty (ExistingF x) = pretty x++-- | Pretty-printer for annotations.+instance (Pretty (f (Fix (AnnotatedF a f))), Show a) => Pretty (AnnotatedF a f (Fix (AnnotatedF a f))) where++  pretty (NoteF ann x) = pretty x ++ "{" ++ show ann ++ "}"+  +-- | A simple expression+expr1 :: Ast+expr1 = Abs (Add (Mul (Real 2) (Complex 0 1)) (Real 1))++-- | Another simple expression+expr2 :: Ast+expr2 = Add (Mul (Var "z") (Var "z")) (Mul (Real 3) (Var "z"))++-- | An expression-with-holes. Holes can be plugged with 'plug'.+--+-- λ> pretty expr3+-- "|(_ + 1.0)|"+--+-- λ> pretty (plug (Var "z") expr3)+--"|(z + 1.0)|"+--+-- λ> :t plug (Var "z") expr3+-- plug (Var "z") expr3 :: Fix AstF -- aka Ast++expr3 :: Holey Ast+expr3 = Existing (AbsF (Existing (AddF Hole (Existing (RealF 1)))))++-- | Real and Complex type tags+data Type = R | C deriving (Eq, Show)++-- | Annotate each subexpression with its type (real or complex).+--+-- λ> pretty expr1+-- "|((2.0 * (0.0 + 1.0i)) + 1.0)|"+--+-- λ> pretty (inferTypes expr1)+-- "|((2.0{R} * (0.0 + 1.0i){C}){C} + 1.0{R}){C}|{R}"++inferTypes :: Ast -> Annotated Type Ast+inferTypes = foldMemo typeOf+  where+    typeOf = \case+      AddF R R -> R+      MulF R R -> R+      AbsF _   -> R+      RealF _  -> R+      _        -> C++-- | Remove all real-valued subexpressions, leaving a 'Hole'+-- in their place.+--+-- λ> pretty expr2+-- "((z * z) + (3.0 * z))"+--+-- λ> pretty (unreal expr2)+-- "((z * z) + (_ * z))"++unreal :: Ast -> Holey Ast+unreal = eraseTypes . punch (\x -> note x == R) . inferTypes+  where+   eraseTypes = natmap (fmap1 erase_)+