使用单例库无法将类型级别列表转换回值级别

时间:2019-07-02 13:01:13

标签: haskell singleton data-kinds

我正在尝试编写合理静态检查的授权系统[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

中找到

1 个答案:

答案 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 ()