加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

haskell – 依赖类约束的不明确类型变量

发布时间:2020-12-14 04:51:07 所属栏目:百科 来源:网络整理
导读:我正在为 Snap web framework编写一个新的身份验证系统,因为内置的身份验证系统不够模块化,并且它具有一些冗余/“自重”功能.但是,这个问题与Snap无关. 在这样做时,我遇到了模糊类型约束的问题.在下面的代码中,对我来说很明显,返回的类型只能是函数类型中的
我正在为 Snap web framework编写一个新的身份验证系统,因为内置的身份验证系统不够模块化,并且它具有一些冗余/“自重”功能.但是,这个问题与Snap无关.

在这样做时,我遇到了模糊类型约束的问题.在下面的代码中,对我来说很明显,返回的类型只能是函数类型中的类型变量b,但GHC抱怨类型不明确.

如何更改以下代码,使背面的类型为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;
               ... }

解决方法

我冒昧地猜测问题的根源在于使用authSnaplet的表达式.原因如下:

?x. x ? :t with authSnaplet 
with authSnaplet
  :: AuthUser u => m b (AuthSnaplet b1 u) a -> m b v a

不介意上下文,我填写了一些虚假实例只是为了加载GHCi中的东西.注意这里的类型变量 – 很多歧义,至少有两个我希望你打算成为同一类型.处理这个的最简单的方法可能是创建一个小的辅助函数,其类型签名可以缩小范围,例如:

withAuthSnaplet :: (AuthUser u)
                => Handler a (AuthSnaplet b u) (Either AuthFailure u) 
                -> Handler a s (Either AuthFailure u)
withAuthSnaplet = with authSnaplet

再次,原谅废话,我实际上并没有安装Snap,这让事情变得尴尬.引入此函数,并使用它代替loginUser中的authSnaplet,允许代码为我键入check.您可能需要稍微调整一下来处理实际的实例约束.

编辑:如果上面的技术不能让你通过某种方式确定b,并假设这些类型确实是为了像写的一样通用,那么b是不可能模糊的,并且没有办法解决它.

与authSnaplet一起使用可以完全从实际类型中删除b,但是在它上面有类约束的多态.这与表达式如show一样模棱两可. read具有依赖于实例的行为,但无法选择一个.

为避免这种情况,您有大约三种选择:

>显式保留不明确的类型,以便在实际的loginUser类型中找到b,而不仅仅是上下文.在您的申请中,出于其他原因,这可能是不合需要的.>通过仅将authSnaplet应用于适当的单态值来删除多态性.如果事先知道类型,就没有模棱两可的余地.这可能意味着在处理程序中放弃一些多态性,但是通过将事物分开,可以将单态限制为仅关注b是什么的代码.>使类约束本身明确无误.如果HasAuth的三个类型参数在某种程度上是相互依赖的,那么对于任何s和u只有一个有效的实例,那么从其他参数到b的函数依赖将是完全合适的.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读