Невозможно преобразовать список на уровне типов обратно в уровень значений с помощью библиотеки синглетонов.

Я пытаюсь написать разумно систему авторизации со статической проверкой [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.


person Saurabh Nanda    schedule 02.07.2019    source источник


Ответы (1)


perms не входит в область действия в теле runAction. Он должен быть явно связан с forall. См. документ на ScopedTypeVariables .

Другая проблема заключается в том, что для «понижения» значения из типов требуется экземпляр SingI.

Ключевая интуиция заключается в том, что forall вводит нерелевантные переменные времени выполнения: если runAction :: forall p. ... не имеет никаких ограничений, runAction @p не может на самом деле зависеть от значения p, он всегда должен делать одно и то же. Диссертация Ричарда Айзенберга, Зависимые типы в Haskell: теория и Практика, содержит более подробную информацию по этому вопросу (раздел 4.2).

Таким образом, тип runAction должен быть примерно таким:

runAction :: forall perms. SingI perms => AppM perms () -> IO ()
person Li-yao Xia    schedule 02.07.2019
comment
Также обратите внимание, что с этой исправленной подписью вы можете просто написать let permissions = fromSing (sing :: SList perms), используя SList из Data.Singletons.Prelude.List. - person K. A. Buhr; 02.07.2019
comment
Или даже просто fromSing (sing :: Sing perms). Дело в том, что вам не нужен отдельный прокси. - person K. A. Buhr; 02.07.2019
comment
Какова интуиция для SingI x ограничения? Тип (или вид?), для которого определены синглтоны? - person Saurabh Nanda; 03.07.2019
comment
Кроме того, почему sing :: Sing perms работает, а sing @perms нет? С другой стороны, кажется, что это работает во фрагменте кода, который выглядит как gist.github. ком/саурабхнанда/ - person Saurabh Nanda; 03.07.2019
comment
Ограничение — это неявный аргумент (со свойством когерентности: нет двух значений одного типа, которые можно было бы передать таким образом). С этой точки зрения SingI x является значением Sing x (доступ к которому вы получаете через sing). Попробуйте sing @_ @perms. sing является поликиновым. т. е. perms может быть любым, и приложения типа делают этот тип явным в качестве аргумента (я полагаю, что есть предложения изменить эту причуду). - person Li-yao Xia; 03.07.2019