2017-09-08 18:24:11 +03:00
{- # LANGUAGE MultiParamTypeClasses, RankNTypes, TypeFamilies, TypeSynonymInstances, UndecidableInstances # -}
2017-07-28 21:37:02 +03:00
module Term
( Term
, TermF
, SyntaxTerm
, SyntaxTermF
, zipTerms
, termSize
, alignCofreeWith
, cofree
, runCofree
2017-09-08 18:24:11 +03:00
, Cofree ( .. )
, extract
, unwrap
, hoistCofree
, CofreeF . headF
, CofreeF . tailF
, CofreeF . CofreeF ()
2017-07-28 21:37:02 +03:00
) where
2015-11-18 22:28:16 +03:00
2017-09-08 18:24:11 +03:00
import Control.Comonad
import Control.Comonad.Cofree.Class
2017-07-28 21:37:02 +03:00
import qualified Control.Comonad.Trans.Cofree as CofreeF
import Control.DeepSeq
import Control.Monad.Free
2016-07-11 22:13:16 +03:00
import Data.Align.Generic
2016-02-29 18:12:34 +03:00
import Data.Functor.Both
2017-08-23 18:49:43 +03:00
import Data.Functor.Classes.Pretty.Generic
2017-07-28 21:37:02 +03:00
import Data.Functor.Foldable
import Data.Maybe
2017-08-22 20:12:00 +03:00
import Data.Proxy
2016-09-12 20:40:22 +03:00
import Data.Record
2016-07-08 20:44:29 +03:00
import Data.These
2017-08-22 20:12:00 +03:00
import Data.Union
2015-11-18 22:28:16 +03:00
import Syntax
2016-10-22 01:06:31 +03:00
-- | A Term with an abstract syntax tree and an annotation.
2017-09-08 18:24:11 +03:00
type Term f = Cofree f
2017-07-28 21:37:02 +03:00
type TermF = CofreeF . CofreeF
2016-05-04 21:37:24 +03:00
2017-09-08 18:24:11 +03:00
infixr 5 :<
data Cofree f a = a :< f ( Cofree f a )
2016-10-22 00:39:13 +03:00
-- | A Term with a Syntax leaf and a record of fields.
2017-07-23 22:56:08 +03:00
type SyntaxTerm fields = Term Syntax ( Record fields )
type SyntaxTermF fields = TermF Syntax ( Record fields )
2016-09-12 20:40:22 +03:00
2017-09-08 18:24:11 +03:00
instance ( NFData ( f ( Cofree f a ) ) , NFData a , Functor f ) => NFData ( Cofree f a ) where
2017-03-14 02:23:33 +03:00
rnf = rnf . runCofree
2017-07-28 21:37:02 +03:00
instance ( NFData a , NFData ( f b ) ) => NFData ( CofreeF . CofreeF f a b ) where
rnf ( a CofreeF .:< s ) = rnf a ` seq ` rnf s ` seq ` ()
2017-03-14 02:23:33 +03:00
2016-01-13 23:35:40 +03:00
-- | Zip two terms by combining their annotations into a pair of annotations.
-- | If the structure of the two terms don't match, then Nothing will be returned.
2016-09-15 00:09:50 +03:00
zipTerms :: ( Traversable f , GAlign f ) => Term f annotation -> Term f annotation -> Maybe ( Term f ( Both annotation ) )
2016-07-11 22:13:16 +03:00
zipTerms t1 t2 = iter go ( alignCofreeWith galign ( const Nothing ) both ( These t1 t2 ) )
2017-07-28 21:37:02 +03:00
where go ( a CofreeF .:< s ) = cofree . ( a CofreeF .:< ) <$> sequenceA s
2015-11-20 04:17:17 +03:00
2016-04-11 22:06:53 +03:00
-- | Return the node count of a term.
2016-09-14 23:12:47 +03:00
termSize :: ( Foldable f , Functor f ) => Term f annotation -> Int
2016-01-13 23:32:03 +03:00
termSize = cata size where
2017-07-28 21:37:02 +03:00
size ( _ CofreeF .:< syntax ) = 1 + sum syntax
2016-07-08 20:44:29 +03:00
2016-07-11 21:33:51 +03:00
-- | Aligns (zips, retaining non-overlapping portions of the structure) a pair of terms.
2016-07-11 21:41:59 +03:00
alignCofreeWith :: Functor f
=> ( forall a b . f a -> f b -> Maybe ( f ( These a b ) ) ) -- ^ A function comparing a pair of structures, returning `Just` the combined structure if they are comparable (e.g. if they have the same constructor), and `Nothing` otherwise. The 'Data.Align.Generic.galign' function is usually what you want here.
2016-09-09 21:46:50 +03:00
-> ( These ( Term f a ) ( Term f b ) -> contrasted ) -- ^ A function mapping a 'These' of incomparable terms into 'Pure' values in the resulting tree.
2016-07-11 21:41:59 +03:00
-> ( a -> b -> combined ) -- ^ A function mapping the input terms’ annotations into annotations in the 'Free' values in the resulting tree.
2016-09-09 21:46:50 +03:00
-> These ( Term f a ) ( Term f b ) -- ^ The input terms.
-> Free ( TermF f combined ) contrasted
2016-07-11 21:33:05 +03:00
alignCofreeWith compare contrast combine = go
where go terms = fromMaybe ( pure ( contrast terms ) ) $ case terms of
2017-09-08 18:24:11 +03:00
These ( a1 :< f1 ) ( a2 :< f2 ) -> wrap . ( combine a1 a2 CofreeF .:< ) . fmap go <$> compare f1 f2
2016-07-11 21:03:05 +03:00
_ -> Nothing
2017-07-28 21:37:02 +03:00
2017-09-08 18:24:11 +03:00
cofree :: CofreeF . CofreeF f a ( Cofree f a ) -> Cofree f a
cofree ( a CofreeF .:< f ) = a :< f
2017-07-28 21:37:02 +03:00
2017-09-08 18:24:11 +03:00
runCofree :: Cofree f a -> CofreeF . CofreeF f a ( Cofree f a )
runCofree ( a :< f ) = a CofreeF .:< f
2017-08-22 19:51:25 +03:00
2017-09-08 18:24:11 +03:00
hoistCofree :: Functor f => ( forall a . f a -> g a ) -> Cofree f a -> Cofree g a
hoistCofree f = go where go ( a :< r ) = a :< f ( fmap go r )
2017-08-22 19:51:25 +03:00
2017-09-08 18:24:11 +03:00
instance Pretty1 f => Pretty1 ( Cofree f ) where
liftPretty p pl = go where go ( a :< f ) = p a <+> liftPretty go ( list . map ( liftPretty p pl ) ) f
2017-08-22 19:52:09 +03:00
2017-09-08 18:24:11 +03:00
instance ( Pretty1 f , Pretty a ) => Pretty ( Cofree f a ) where
2017-08-23 18:52:00 +03:00
pretty = liftPretty pretty prettyList
2017-08-22 20:12:00 +03:00
instance Apply1 Pretty1 fs => Pretty1 ( Union fs ) where
liftPretty p pl = apply1 ( Proxy :: Proxy Pretty1 ) ( liftPretty p pl )
2017-09-08 18:24:11 +03:00
type instance Base ( Cofree f a ) = CofreeF . CofreeF f a
instance Functor f => Recursive ( Cofree f a ) where project = runCofree
instance Functor f => Corecursive ( Cofree f a ) where embed = cofree
instance Functor f => Comonad ( Cofree f ) where
extract ( a :< _ ) = a
duplicate w = w :< fmap duplicate ( unwrap w )
extend f = go where go w = f w :< fmap go ( unwrap w )
instance Functor f => Functor ( Cofree f ) where
fmap f = go where go ( a :< r ) = f a :< fmap go r
instance Functor f => ComonadCofree f ( Cofree f ) where
unwrap ( _ :< as ) = as
{- # INLINE unwrap # -}
instance ( Eq ( f ( Cofree f a ) ) , Eq a ) => Eq ( Cofree f a ) where
a1 :< f1 == a2 :< f2 = a1 == a2 && f1 == f2
instance ( Show ( f ( Cofree f a ) ) , Show a ) => Show ( Cofree f a ) where
showsPrec d ( a :< f ) = showParen ( d > 5 ) $ showsPrec 6 a . showString " :< " . showsPrec 5 f