generics-mrsop-2.2.0: src/Generics/MRSOP/Examples/RoseTree.hs
{-# 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)