{-# LANGUAGE CPP #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneKindSignatures #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fplugin=IfCt.Plugin #-} {-# OPTIONS_GHC -dcore-lint #-} module Main where -- base import Data.Maybe ( mapMaybe ) import Data.Kind ( Constraint, Type ) import System.Exit ( exitFailure, exitSuccess ) #if MIN_VERSION_ghc(9,4,0) import GHC.Exts ( withDict ) #endif -- IfCt import Data.Constraint.If ( IfCt(ifCt) ) -------------------------------------------------------------------------------- type MyShow :: Type -> Constraint class MyShow a where myShow :: a -> String instance MyShow Int where myShow = show myShowAnything :: forall a. IfCt ( MyShow a ) => a -> String myShowAnything = ifCt @( MyShow a ) yes no where yes :: MyShow a => a -> String yes = myShow no :: a -> String no _ = "<>" -- Should use the "MyShow Int" instance. test1 :: String test1 = myShowAnything ( 123 :: Int ) -- No "MyShow ( Int -> Int -> Int )" instance. test2 :: String test2 = myShowAnything ( (+) :: Int -> Int -> Int ) data A = A myShowA :: IfCt ( MyShow A ) => String myShowA = myShowAnything A #if MIN_VERSION_ghc(9,4,0) -- Should use the instance locally provided by "withDict". test3 :: String test3 = withDict @( A -> String ) @( MyShow A ) ( \ _ -> "A" ) myShowA #endif -- No "MyShow A" instance. test4 :: String test4 = myShowA -------------------------------------------------------------------------------- data Test where Test :: ( Show a, Eq a ) => { testName :: String , testActual :: a , testExpected :: a } -> Test runTest :: Test -> Maybe String runTest ( Test { testName, testActual, testExpected } ) | testActual == testExpected = Nothing | otherwise = Just $ "\n" <> "Test '" <> testName <> "' failed.\n" <> "Expected: " <> show testExpected <> "\n" <> " Actual: " <> show testActual tests :: [ Test ] tests = [ Test "test1" test1 "123" , Test "test2" test2 "<>" #if MIN_VERSION_ghc(9,4,0) , Test "test3" test3 "A" #endif , Test "test4" test4 "<>" ] main :: IO () main = do let results :: [ String ] results = mapMaybe runTest tests case results of [] -> exitSuccess _ -> putStrLn ( unlines results ) *> exitFailure