我正在尝试编写合理静态检查的授权系统[1],目前正在努力编写一个将从类型级注释/幻像中提取所需权限的函数到价值水平。
{-# LANGUAGE DataKinds, GADTs, ScopedTypeVariables #-}
module Try5 where
import Control.Monad.Reader
import Data.Singletons
import Data.Singletons.TH
data Permission = PermA
| PermB
deriving (Eq, Show)
$(genSingletons [''Permission])
data Env = Env
newtype AppM (perms :: [Permission]) a = AppM (ReaderT Env IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadReader Env)
-- other functions for constructing an action in `AppM perms`
-- have been removed for brevity
runAction :: AppM (perms :: [Permission]) () -> IO ()
runAction _ = do
let permissions :: [Permission] = fromSing $ singByProxy (Proxy :: Proxy (perms :: [Permission]))
putStrLn $ "Huzzah, I freed the permissions from the type-level cage: " <> (show permissions)
pure ()
• Ambiguous type variable ‘a0’ arising from a use of ‘singByProxy’
prevents the constraint ‘(SingI a0)’ from being solved.
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance forall a (n1 :: a) (n2 :: [a]).
(SingI n1, SingI n2) =>
SingI (n1 : n2)
-- Defined in ‘singletons-2.4.1:Data.Singletons.Prelude.Instances’
instance SingI '[]
-- Defined in ‘singletons-2.4.1:Data.Singletons.Prelude.Instances’
• In the second argument of ‘($)’, namely
‘singByProxy (Proxy :: Proxy (perms :: [Permission]))’
In the expression:
fromSing $ singByProxy (Proxy :: Proxy (perms :: [Permission]))
In a pattern binding:
permissions :: [Permission]
= fromSing $ singByProxy (Proxy :: Proxy (perms :: [Permission]))
|
24 | let permissions :: [Permission] = fromSing $ singByProxy (Proxy :: Proxy (perms :: [Permission]))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[1]更多上下文可以在https://stackoverflow.com/a/56828016/534481
中找到答案 0 :(得分:3)
perms
不在runAction
主体的范围内。它需要与forall
明确绑定。参见doc on ScopedTypeVariables
。
另一个问题是从类型中“删除”值需要一个SingI
实例。
关键直觉是forall
引入了与运行时无关的变量:如果runAction :: forall p. ...
没有任何约束,则runAction @p
实际上不能依赖于p
的值,它必须始终做同样的事情。理查德·艾森伯格(Richard Eisenberg)的论文Dependent types in Haskell: Theory and Practice对此事有更多详细信息(第4.2节)。
因此runAction
的类型应如下所示:
runAction :: forall perms. SingI perms => AppM perms () -> IO ()