fin-0.0.1: test/Inspection.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -O -fplugin Test.Inspection.Plugin #-}
module Main (main) where
import Data.Function (fix)
import Data.Proxy (Proxy (..))
import Data.Tagged (Tagged (..), retag)
import Data.Type.Equality
import GHC.Generics (Generic)
import Test.Inspection
import qualified Data.Fin as F
import qualified Data.Fin.Enum as E
import qualified Data.Type.Nat as N
import Unsafe.Coerce (unsafeCoerce)
-------------------------------------------------------------------------------
-- InlineInduction
-------------------------------------------------------------------------------
-- | This doesn't evaluate compile time.
lhsInline :: Int
lhsInline = unTagged (N.inlineInduction1 (pure 0) (retag . fmap succ) :: Tagged N.Nat5 Int)
-- | This doesn't evaluate compile time.
lhsNormal :: Int
lhsNormal = unTagged (N.induction1 (pure 0) (retag . fmap succ) :: Tagged N.Nat5 Int)
rhs :: Int
rhs = 5
inspect $ 'lhsInline === 'rhs
inspect $ 'lhsNormal =/= 'rhs
-------------------------------------------------------------------------------
-- Enum
-------------------------------------------------------------------------------
-- | Note: GHC 8.0 (but not GHC 8.2?) seems to be
-- so smart, it reuses dictionary value.
--
-- Therefore, we define own local Ordering'
data Ordering' = LT' | EQ' | GT' deriving (Generic)
lhsEnum :: Ordering' -> F.Fin N.Nat3
lhsEnum = E.gfrom
rhsEnum :: Ordering' -> F.Fin N.Nat3
rhsEnum LT' = F.Z
rhsEnum EQ' = F.S F.Z
rhsEnum GT' = F.S (F.S F.Z)
inspect $ 'lhsEnum ==- 'rhsEnum
-------------------------------------------------------------------------------
-- Proofs
-------------------------------------------------------------------------------
lhsProof :: forall n. N.SNatI n => F.Fin (N.Mult n N.Nat1) -> F.Fin n
lhsProof x = case N.proofMultNOne :: N.Mult n N.Nat1 :~: n of
Refl -> x
rhsProof :: forall n. N.SNatI n => F.Fin (N.Mult n N.Nat1) -> F.Fin n
rhsProof x = unsafeCoerce x
inspect $ 'lhsProof ==- 'rhsProof
-------------------------------------------------------------------------------
-- unfoldedFix
-------------------------------------------------------------------------------
foldrF :: (a -> b -> b) -> b -> ([a] -> b) -> [a] -> b
foldrF _f z _go [] = z
foldrF f _z go (x : xs) = f x (go xs)
superfold :: [Int] -> Int
superfold = N.unfoldedFix (Proxy :: Proxy N.Nat5) (foldrF (+) 0)
-- Note: we need to write list explicitly, cannot use shorthand [1..4]
-- 'enumFromTo' is a recursive function!
--
-- Try to change [1,2,4,] to [1..4] to see the generated core :)
lhsFold :: Int
lhsFold = superfold [1,2,3,4]
lhsFold' :: Int
lhsFold' = fix (foldrF (+) 0) [1,2,3,4]
rhsFold :: Int
rhsFold = 10
inspect $ 'lhsFold === 'rhsFold
inspect $ 'lhsFold' =/= 'rhsFold
lhsUnfold :: (a -> a) -> a
lhsUnfold f = N.unfoldedFix (Proxy :: Proxy N.Nat3) f
rhsUnfold :: (a -> a) -> a
rhsUnfold f = f (f (f (fix f)))
inspect $ 'lhsUnfold === 'rhsUnfold
-------------------------------------------------------------------------------
-- Power
-------------------------------------------------------------------------------
power :: forall n. N.InlineInduction n => Proxy n -> Int -> Int
power _ k = unTagged impl where
impl :: Tagged n Int
impl = N.inlineInduction1 (Tagged 1) $ \(Tagged rec') -> Tagged (rec' * k)
lhsPower5 :: Int -> Int
lhsPower5 = power (Proxy :: Proxy N.Nat5)
rhsPower5 :: Int -> Int
rhsPower5 k = k * k * k * k * k
inspect $ 'lhsPower5 === 'rhsPower5
-------------------------------------------------------------------------------
-- Main to make GHC happy
-------------------------------------------------------------------------------
main :: IO ()
main = return ()