content: {-# LANGUAGE MultiWayIf #-}
  {-# LANGUAGE TemplateHaskell #-}
  {-# LANGUAGE TypeApplications #-}
  {-# OPTIONS_GHC -O2 -fplugin Test.Inspection.Plugin #-}
  module Fusion
  ( tests
  ) where
  
  import Control.Algebra
  import Control.Carrier.Error.Either
  import Control.Carrier.State.Strict
  import Gen
  import Hedgehog
  import Test.Inspection as Inspection hiding (property, (===))
  
  tests :: TestTree
  tests = testGroup "fusion"
    [ testProperty "eliminates StateCs" . property $
      failureOf $(inspectTest $ 'countDown `doesNotUse` ''StateC)
      === Nothing
    , testProperty "eliminates nested StateCs" . property $
      failureOf $(inspectTest $ 'countBoth `doesNotUse` ''StateC)
      === Nothing
    , testProperty "eliminates catch and throw" . property $
      failureOf $(inspectTest $ 'throwing `doesNotUse` ''ErrorC)
      === Nothing
    , testProperty "eliminates calls to alg" . property $
      failureOf $(inspectTest $ 'countDown `doesNotUse` 'alg)
      === Nothing
    ]
  
  
  failureOf :: Inspection.Result -> Maybe String
  failureOf (Success _) = Nothing
  failureOf (Failure f) = Just f
  
  
  countDown :: Int -> (Int, Int)
  countDown start = run . runState start $ go
    where go = get >>= \n -> if n <= 0 then pure n else modify @Int pred *> go
  
  countBoth :: Int -> (Int, (Float, ()))
  countBoth n = run . runState n . runState (fromIntegral n) $ go where
    go = do
      n <- get @Int
      if
        | n == 0         -> pure ()
        | n `mod` 2 == 0 -> modify @Float (+ 1) *> modify @Int pred *> go
        | otherwise      -> modify @Int pred    *> go
  
  throwing :: Int -> Either Int String
  throwing n = run $ runError go
    where go = if n > 10 then throwError @Int 42 else pure "fine"
  