I'm trying to build a statically-typed authorisation system and have the following working code-snippet:
{-# LANGUAGE DataKinds, ScopedTypeVariables, TypeFamilies #-}
module Try where
import Data.Singletons.TH
data FeatureFlag = Feature1 | Feature2 deriving (Eq, Show)
$(genSingletons [''FeatureFlag])
type family Feature (f :: FeatureFlag) (fs :: [FeatureFlag]) where
Feature f '[] = 'False
Feature f (f:fs) = 'True
Feature f (q:fs) = Feature f fs
lockedFeatureAction :: (MonadIO (m fs), Feature 'Feature1 fs ~ 'True) => m fs ()
lockedFeatureaction = undefined
checkFeatureFlagsAndRun :: forall (fs :: [FeatureFlag]) . (SingI fs) => Proxy fs -> AppM fs () -> IO ()
checkFeatureFlagsAndRun = undefined
And this is how it gets used:
ghci> checkFeatureFlagsAndRun (Proxy :: Proxy '[ 'Feature1]) lockedFeatureAction
All is good when the types and stars align. However, if the types don't align, the error message is a classic Sherlock Holmes "whodunnit":
ghci> checkFeatureFlagsAndRun (Proxy :: Proxy '[ 'Feature2]) lockedFeatureAction
<interactive>:462:32: error:
• Couldn't match type ‘'False’ with ‘'True’
arising from a use of ‘lockedFeatureAction’
• In the second argument of ‘checkFeatureFlagsAndRun’, namely ‘lockedFeatureAction’
In the expression: checkFeatureFlagsAndRun (Proxy :: Proxy '[ 'Feature2]) lockedFeatureAction
In an equation for ‘it’: it = checkFeatureFlagsAndRun (Proxy :: Proxy '[ 'Feature2]) lockedFeatureAction
I tried searching and stumbled upon https://kcsongor.github.io/report-stuck-families/, which talks of TypeError I tried using it like this, but it didn't work:
type family Feature (f :: FeatureFlag) (fs :: [FeatureFlag]) where
Feature f '[] = TypeError "Could not satisfy FeatureFlag conditions"
Feature f (f:fs) = 'True
Feature f (q:fs) = Feature f fs
-- • Expected kind ‘ghc-prim-0.5.2.0:GHC.Types.Symbol -> Bool’,
-- but ‘TypeError’ has kind ‘*’
-- • In the type ‘TypeError "Could not satisfy FeatureFlag conditions"’
-- In the type family declaration for ‘Feature’
-- |
-- 19 | Feature f '[] = TypeError "Could not satisfy FeatureFlag conditions"
-- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What is the correct way to use TypeError? Alternatively, is there any other way to get better error messages?