Я пишу новую систему аутентификации для Snap web framework, потому что встроенный модуль не является достаточно модульным, и у него есть некоторые функции, которые являются избыточными/«мертвыми» для моего приложения , Однако эта проблема не связана с Snap.Неоднозначные переменные типа для зависимых ограничений класса
При этом я столкнулся с проблемой с неопределенными ограничениями типа. В следующем коде мне кажется очевидным, что тип back
может быть только переменной типа b
в типе функций, однако GHC жалуется, что тип неоднозначен.
Как изменить следующий код так, чтобы тип back
был b
, без использования, например, ScopedTypeVariables
(потому что проблема связана с ограничением, а не с слишком общими типами)? Существует ли какая-то функциональная зависимость?
Соответствующие классы типа:
data AuthSnaplet b u =
AuthSnaplet
{ _backend :: b
, _activeUser :: Maybe u
}
-- data-lens-template:Data.Lens.Template.makeLens
-- data-lens:Data.Lens.Common.Lens
-- generates: backend :: Lens (AuthSnaplet b u) b
makeLens ''AuthSnaplet
-- Some encrypted password
newtype Password =
Password
{ passwordData :: ByteString
}
-- data-default:Data.Default.Default
class Default u => AuthUser u where
userLogin :: Lens u Text
userPassword :: Lens u Password
class AuthUser u => AuthBackend b u where
save :: MonadIO m => b -> u -> m u
lookupByLogin :: MonadIO m => b -> Text -> m (Maybe u)
destroy :: MonadIO m => b -> u -> m()
-- snap:Snap.Snaplet.Snaplet
class AuthBackend b u => HasAuth s b u where
authSnaplet :: Lens s (Snaplet (AuthSnaplet b u))
Код, который не удается:
-- snap:Snap.Snaplet.with :: Lens v (Snaplet v') -> m b v' a -> m b v a
-- data-lens-fd:Data.Lens.access :: MonadState a m => Lens a b -> m b
loginUser :: HasAuth s b u
=> Text -> Text -> Handler a s (Either AuthFailure u)
loginUser uname passwd = with authSnaplet $ do
back <- access backend
maybeUser <- lookupByLogin back uname -- !!! type of back is ambiguous !!!
-- ... For simplicity's sake, let's say the function ends like this:
return . Right . fromJust $ maybeUser
Полная ошибка:
src/Snap/Snaplet/Authentication.hs:105:31:
Ambiguous type variables `b0', `u0' in the constraint:
(HasAuth s b0 u0) arising from a use of `authSnaplet'
Probable fix: add a type signature that fixes these type variable(s)
In the first argument of `with', namely `authSnaplet'
In the expression: with authSnaplet
In the expression:
with authSnaplet
$ do { back <- access backend;
maybeUser <- lookupByLogin back uname;
... }
src/Snap/Snaplet/Authentication.hs:107:16:
Ambiguous type variable `b0' in the constraint:
(AuthBackend b0 u) arising from a use of `lookupByLogin'
Probable fix: add a type signature that fixes these type variable(s)
In a stmt of a 'do' expression:
maybeUser <- lookupByLogin back uname
In the second argument of `($)', namely
`do { back <- access backend;
maybeUser <- lookupByLogin back uname;
... }'
In the expression:
with authSnaplet
$ do { back <- access backend;
maybeUser <- lookupByLogin back uname;
... }
Ведущие подчеркивания в Haskell обычно обозначают значение «не заботятся». Насколько я знаю, это чисто стилистический (компилятор просто не предупреждает о неиспользуемых значениях), но я бы не использовал их как функции доступа. –
AuthBackend - это тип многопараметрического типа. Возможно, вам нужна функциональная зависимость, например, «class [...] AuthBackend b u | u -> b». Это означает, что для любого типа «u» может быть только один соответствующий тип «b», который является экземпляром этого класса. –
@Paul Johnson: Ведущие подчеркивания - это то, как вы получаете линзы с использованием «шаблона данных-объективов». Они никогда не используются напрямую. Кроме того, я думаю, что отказ от предупреждения относится только к таким параметрам. – ehird