{-# LANGUAGE TypeApplications #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE PatternSynonyms #-} {-# OPTIONS_GHC -Wno-missing-pattern-synonym-signatures #-} {-# OPTIONS_GHC -Wno-incomplete-patterns #-} {-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-unused-top-binds #-} -- |This module is analogous to 'Generics.MRSOP.Examples.RoseTreeTH', -- but we use no Template Haskell here. module Generics.MRSOP.Examples.RoseTree ( -- * Non-standard Rose-Tree datatype -- $exp01 R -- ** Family Structure -- $exp02 , ListCode,RTCode,CodesRose,FamRose -- ** 'Family' Instance -- $exp03 -- * Generic Combinators -- $exp04 , testEq , normalize , sumTree ) where import Data.Function (on) import Generics.MRSOP.Base import Generics.MRSOP.Opaque -- $exp01 -- -- Suppose we we have a mutually recursive family -- consisting of RoseTree's with some redundant constructor: -- | Non-standard Rose-tree datatype. data R a = a :>: [R a] | Leaf a deriving Show value1, value2, value3 :: R Int value1 = 1 :>: [2 :>: [], 3 :>: []] value2 = 1 :>: [2 :>: [] , Leaf 12] value3 = 3 :>: [Leaf 23 , value1 , value2] -- $exp02 -- -- The @R Int@ family has many components, that must be encoded in -- the generics-mrsop format. These are: -- |Codes for the @[R Int]@ type. type ListCode = '[ '[] , '[ 'I ('S 'Z) , 'I 'Z] ] -- |Codes for the @R Int@ type type RTCode = '[ '[ 'K 'KInt , 'I 'Z] , '[ 'K 'KInt] ] -- |All codes packed in a type-level list type CodesRose = '[ListCode , RTCode] -- |The types corresponding the the codes in 'CodesRose' -- appear in the same order. type FamRose = '[ [R Int] , R Int] -- ** Instance Decl -- $exp03 -- -- Which in turn, allows us to write the 'Family' instance for -- @R Int@. The @instance Family Singl FamRose CodesRose@ states that -- the types in 'FamRose' follow the codes in 'CodesRose' with -- its opaque parts represented by 'Singl' Check the source code for more details -- on the instance. -- -- It is worth mentioning that 'Generics.MRSOP.TH.deriveFamily' will derive -- this code automatically. -- -- >instance Family Singl FamRose CodesRose where -- > sfrom' (SS SZ) (El (a :>: as)) = Rep $ Here (NA_K (SInt a) :* NA_I (El as) :* Nil) -- > sfrom' (SS SZ) (El (Leaf a)) = Rep $ There (Here (NA_K (SInt a) :* Nil)) -- > sfrom' SZ (El []) = Rep $ Here Nil -- > sfrom' SZ (El (x:xs)) = Rep $ There (Here (NA_I (El x) :* NA_I (El xs) :* Nil)) -- > sfrom' _ _ = error "unreachable" -- > -- > sto' SZ (Rep (Here Nil)) -- > = El [] -- > sto' SZ (Rep (There (Here (NA_I (El x) :* NA_I (El xs) :* Nil)))) -- > = El (x : xs) -- > sto' (SS SZ) (Rep (Here (NA_K (SInt a) :* NA_I (El as) :* Nil))) -- > = El (a :>: as) -- > sto' (SS SZ) (Rep (There (Here (NA_K (SInt a) :* Nil)))) -- > = El (Leaf a) -- > sto' _ _ = error "unreachable" instance Family Singl FamRose CodesRose where sfrom' (SS SZ) (El (a :>: as)) = Rep $ Here (NA_K (SInt a) :* NA_I (El as) :* Nil) sfrom' (SS SZ) (El (Leaf a)) = Rep $ There (Here (NA_K (SInt a) :* Nil)) sfrom' SZ (El []) = Rep $ Here Nil sfrom' SZ (El (x:xs)) = Rep $ There (Here (NA_I (El x) :* NA_I (El xs) :* Nil)) sfrom' _ _ = error "unreachable" sto' SZ (Rep (Here Nil)) = El [] sto' SZ (Rep (There (Here (NA_I (El x) :* NA_I (El xs) :* Nil)))) = El (x : xs) sto' (SS SZ) (Rep (Here (NA_K (SInt a) :* NA_I (El as) :* Nil))) = El (a :>: as) sto' (SS SZ) (Rep (There (Here (NA_K (SInt a) :* Nil)))) = El (Leaf a) sto' _ _ = error "unreachable" instance HasDatatypeInfo Singl FamRose CodesRose where datatypeInfo _ SZ = ADT "module" (Name "[]" :@: (Name "R" :@: Name "Int")) $ (Constructor "[]") :* (Infix ":" RightAssociative 5) :* Nil datatypeInfo _ (SS SZ) = ADT "module" (Name "R" :@: Name "Int") $ (Infix ":>:" NotAssociative 0) :* (Constructor "Leaf") :* Nil datatypeInfo _ _ = error "unreachable" -- $exp04 -- -- Next, we showcase some of the simpler generic combinators provided -- out of the box with /generics-mrsop/ instance Eq (R Int) where (==) = geq eqSingl `on` (into @FamRose) -- |We can use generic equality out of the box: -- -- > instance Eq (R Int) where -- > (==) = geq eqSingl `on` (into @FamRose) -- -- If we run 'testEq' it must return @True@, naturally. testEq :: Bool testEq = value1 == value1 && value2 /= value1 pattern RInt_ = SS SZ -- |Here is an example of 'compos'; used to substitute the redundant 'Leaf' -- constructor by its standard rose tree representation. -- -- > normalize :: R Int -> R Int -- > normalize = unEl . go (SS SZ) . into -- > where -- > go :: forall iy. (IsNat iy) => SNat iy -> El FamRose iy -> El FamRose iy -- > go (SS SZ) (El (Leaf a)) = El (a :>: []) -- (SS SZ) is the index of 'R Int' in 'CodesRose' -- > go _ x = compos go x -- -- Then, for example, -- -- > normalize (42 :>: [Leaf 10 , 15 :>: [Leaf 20]]) == 42 :>: [10 :>: [] , 15 :>: [20 :>: []]] -- normalize :: R Int -> R Int normalize = unEl . go (SS SZ) . into where go :: forall iy. (IsNat iy) => SNat iy -> El FamRose iy -> El FamRose iy go RInt_ (El (Leaf a)) = El (a :>: []) go _ x = compos go x -- |Another generic combinator is 'crush'. We can 'crush' a rose tree and compute the sum -- of all the ints stored within said tree. -- -- > sumTree :: R Int -> Int -- > sumTree = crush k sum . (into @FamRose) -- > where k :: Singl x -> Int -- > k (SInt n) = n -- sumTree :: R Int -> Int sumTree = crush k sum . (into @FamRose) where k :: Singl x -> Int k (SInt n) = n testSum :: Bool testSum = sumTree value3 == sumTree (normalize value3)