无法使用单例库将类型级列表转换回值级
Unable to convert type-level list back to value-level using singletons library
我正在尝试编写 合理地 静态检查授权系统 [1],并且目前正在努力编写一个将从类型中提取所需权限的函数-level annotation/phantom 到 value-level.
{-# 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] 可以在
找到更多上下文
perms
不在 runAction
正文的范围内。它需要与 forall
显式绑定。见 doc on ScopedTypeVariables
.
另一个问题是 "demote" 来自类型的值需要一个 SingI
实例。
关键的直觉是 forall
引入了 run-time 不相关的变量:如果 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 ()
我正在尝试编写 合理地 静态检查授权系统 [1],并且目前正在努力编写一个将从类型中提取所需权限的函数-level annotation/phantom 到 value-level.
{-# 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] 可以在
perms
不在 runAction
正文的范围内。它需要与 forall
显式绑定。见 doc on ScopedTypeVariables
.
另一个问题是 "demote" 来自类型的值需要一个 SingI
实例。
关键的直觉是 forall
引入了 run-time 不相关的变量:如果 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 ()