if-instance 0.2.0.0 → 0.2.1.0
raw patch · 4 files changed
+78/−33 lines, 4 filesdep ~ghc-tcplugin-apiPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: ghc-tcplugin-api
API changes (from Hackage documentation)
Files
- changelog.md +6/−2
- if-instance.cabal +2/−2
- src/IfSat/Plugin.hs +32/−20
- test/Main.hs +38/−9
changelog.md view
@@ -1,10 +1,14 @@+# Version 0.2.1.0 (2021-08-31) + +- Require `ghc-tcplugin-api >= 0.5.1.0`. + # Version 0.2.0.0 (2021-08-31) -- Add a type family 'IsSat :: Constraint -> Bool' +- Add a type family `IsSat :: Constraint -> Bool` that computes whether a type-family is satisfied in the current context. -- Rename 'IfCt' to 'IfSat' +- Rename `IfCt` to `IfSat`. # Version 0.1.0.0 (2021-08-30)
if-instance.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: if-instance -version: 0.2.0.0 +version: 0.2.1.0 synopsis: Branch on whether a constraint is satisfied license: BSD-3-Clause build-type: Simple @@ -85,7 +85,7 @@ build-depends: ghc-tcplugin-api - >= 0.5.0.0 && < 0.6, + >= 0.5.1.0 && < 0.6, exposed-modules: Data.Constraint.If
src/IfSat/Plugin.hs view
@@ -183,15 +183,15 @@ let r, a, b :: CoreBndr r = mkTyVar r_name liftedTypeKind - a = mkLocalId a_name Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty tru) $ mkInvisFunTyMany ct_ty r_ty ) - b = mkWildValBinder Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty fls) r_ty ) + a = mkLocalId a_name Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty True ) $ mkInvisFunTyMany ct_ty r_ty ) + b = mkWildValBinder Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty False) r_ty ) r_ty :: Type r_ty = mkTyVarTy r pure . EvExpr $ mkCoreConApps ( classDataCon ifSatClass ) [ Type ct_ty , mkCoreLams [ r, a, b ] - ( mkCoreApps ( Var a ) [ sat_co_expr defs ct_ty tru, ct_evTerm ] ) + ( mkCoreApps ( Var a ) [ sat_co_expr defs ct_ty True, ct_evTerm ] ) ] -- Evidence term for @IfSat ct@ when @ct@ isn't satisfied. @@ -203,37 +203,46 @@ let r, a, b :: CoreBndr r = mkTyVar r_name liftedTypeKind - a = mkWildValBinder Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty tru) $ mkInvisFunTyMany ct_ty r_ty ) - b = mkLocalId b_name Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty fls) r_ty ) + a = mkWildValBinder Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty True ) $ mkInvisFunTyMany ct_ty r_ty ) + b = mkLocalId b_name Many ( mkInvisFunTyMany (sat_eqTy defs ct_ty False) r_ty ) r_ty :: Type r_ty = mkTyVarTy r pure . EvExpr $ mkCoreConApps ( classDataCon ifSatClass ) [ Type ct_ty , mkCoreLams [ r, a, b ] - ( mkCoreApps ( Var b ) [ sat_co_expr defs ct_ty fls ] ) + ( mkCoreApps ( Var b ) [ sat_co_expr defs ct_ty False ] ) ] -fls, tru :: Type -fls = mkTyConTy promotedFalseDataCon -tru = mkTyConTy promotedTrueDataCon - --- @ sat_eqTy defs ct_ty rhs @ represents the type @ IsSat ct ~ rhs @. -sat_eqTy :: PluginDefs -> Type -> Type -> Type -sat_eqTy ( PluginDefs { isSatTyCon } ) ct_ty rhs +-- @ sat_eqTy defs ct_ty b @ represents the type @ IsSat ct ~ b @. +sat_eqTy :: PluginDefs -> Type -> Bool -> Type +sat_eqTy ( PluginDefs { isSatTyCon } ) ct_ty booly = mkTyConApp eqTyCon [ boolTy, mkTyConApp isSatTyCon [ct_ty], rhs ] + where + rhs :: Type + rhs = if booly then tru else fls --- @ sat_co_expr defs ct_ty rhs @ is an expression of type @ IsSat ct ~ rhs @. -sat_co_expr :: PluginDefs -> Type -> Type -> EvExpr -sat_co_expr ( PluginDefs { isSatTyCon } ) ct_ty rhs +-- @ sat_co_expr defs ct_ty b @ is an expression of type @ IsSat ct ~ b @. +sat_co_expr :: PluginDefs -> Type -> Bool -> EvExpr +sat_co_expr ( PluginDefs { isSatTyCon } ) ct_ty booly = mkCoreConApps eqDataCon [ Type boolTy , Type $ mkTyConApp isSatTyCon [ ct_ty ] , Type rhs - , Coercion $ mkPluginUnivCo "IfSat: IsSat" Nominal ( mkTyConApp isSatTyCon [ct_ty] ) rhs + , Coercion $ + mkPluginUnivCo ( "IfSat :" <> show booly ) + Nominal + ( mkTyConApp isSatTyCon [ct_ty] ) rhs ] + where + rhs :: Type + rhs = if booly then tru else fls +fls, tru :: Type +fls = mkTyConTy promotedFalseDataCon +tru = mkTyConTy promotedTrueDataCon + -------------------------------------------------------------------------------- rewriter :: PluginDefs -> UniqFM TyCon TcPluginRewriter @@ -255,15 +264,18 @@ solveSimpleGivens givens -- Try to solve 'ct', using both Givens and top-level instances. residual_wc <- solveSimpleWanteds ( unitBag ct ) + -- When there are residual Wanteds, we couldn't solve the constraint. let + is_sat :: Bool + is_sat = isEmptyWC residual_wc sat :: Type sat - | isEmptyWC residual_wc + | is_sat = mkTyConTy promotedTrueDataCon | otherwise = mkTyConTy promotedFalseDataCon - pure $ mkTyFamAppReduction "IfSat: IsSat" Nominal isSatTyCon [ct_ty] sat - tcPluginTrace "IfSat rewriter }" empty + pure $ mkTyFamAppReduction ( "IsSat: " <> show is_sat ) Nominal isSatTyCon [ct_ty] sat + tcPluginTrace "IfSat rewriter }" ( ppr redn ) pure $ TcPluginRewriteTo redn [] isSatRewriter _ _ _ = pure TcPluginNoRewrite
test/Main.hs view
@@ -1,7 +1,9 @@ +{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE CPP #-} -{-# LANGUAGE GADTs #-} +{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneKindSignatures #-} @@ -19,17 +21,25 @@ ( Constraint, Type ) import System.Exit ( exitFailure, exitSuccess ) -#if MIN_VERSION_ghc(9,4,0) +#if MIN_VERSION_ghc(9,3,0) import GHC.Exts ( withDict ) #endif -- IfSat import Data.Constraint.If - ( IfSat(ifSat) ) + ( IfSat(ifSat), IsSat ) -------------------------------------------------------------------------------- +type BoolI :: Bool -> Constraint +class BoolI b where + boolI :: Bool +instance BoolI True where + boolI = True +instance BoolI False where + boolI = False + type MyShow :: Type -> Constraint class MyShow a where myShow :: a -> String @@ -49,28 +59,43 @@ test1 :: String test1 = myShowAnything ( 123 :: Int ) +test1b :: Bool +test1b = boolI @( IsSat ( MyShow Int ) ) + -- No "MyShow ( Int -> Int -> Int )" instance. test2 :: String test2 = myShowAnything ( (+) :: Int -> Int -> Int ) +test2b :: Bool +test2b = boolI @( IsSat ( MyShow ( Int -> Int -> Int ) ) ) + data A = A myShowA :: IfSat ( MyShow A ) => String myShowA = myShowAnything A -#if MIN_VERSION_ghc(9,4,0) +#if MIN_VERSION_ghc(9,3,0) -- Should use the instance locally provided by "withDict". test3 :: String test3 = withDict @( A -> String ) @( MyShow A ) ( \ _ -> "A" ) myShowA + +test3b :: Bool +test3b = + withDict @( A -> String ) @( MyShow A ) + ( \ _ -> "A" ) + ( boolI @( IsSat ( MyShow A ) ) ) #endif -- No "MyShow A" instance. test4 :: String test4 = myShowA +test4b :: Bool +test4b = boolI @( IsSat ( MyShow A ) ) + -------------------------------------------------------------------------------- data Test where @@ -95,12 +120,16 @@ tests :: [ Test ] tests = - [ Test "test1" test1 "123" - , Test "test2" test2 "<<no MyShow instance>>" -#if MIN_VERSION_ghc(9,4,0) - , Test "test3" test3 "A" + [ Test "test1" test1 "123" + , Test "test1b" test1b True + , Test "test2" test2 "<<no MyShow instance>>" + , Test "test2b" test2b False +#if MIN_VERSION_ghc(9,3,0) + , Test "test3" test3 "A" + , Test "test3b" test3b True #endif - , Test "test4" test4 "<<no MyShow instance>>" + , Test "test4" test4 "<<no MyShow instance>>" + , Test "test4b" test4b False ] main :: IO ()