4

Snap Webフレームワーク用の新しい認証システムを作成しています。これは、組み込みの認証システムが十分にモジュール化されておらず、アプリケーションにとって冗長/「自重」であるいくつかの機能を備えているためです。ただし、この問題はスナップとはまったく関係ありません。

そうしている間、私はあいまいな型制約の問題にぶつかりました。次のコードでは、の型は関数型backの型変数にしかなり得ないことは明らかですがb、GHCは型があいまいであると不平を言っています。

たとえばを使用せずに、タイプbackがになるように次のコードを変更するにはどうすればよいですか(問題は制約にあり、一般的なタイプが多すぎることではないため)?どこかに必要な機能依存性はありますか?bScopedTypeVariables

関連する型クラス:

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;
               ... }
4

1 に答える 1

3

あなたの問題の根源は表現にあると思いますwith authSnaplet。理由は次のとおりです。

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

コンテキストは気にしないでください。GHCi に何かをロードするためだけに、いくつかの偽のインスタンスを入力しました。ここの型変数に注意してください。多くのあいまいさがあり、少なくとも 2 つは同じ型であると思われます。これを処理する最も簡単な方法は、おそらく、物事を少し絞り込む型シグネチャを持つ小さな補助関数を作成することです。

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

繰り返しますが、ナンセンスなことをお許しください。私は実際には現時点で Snap をインストールしていません。この関数を導入し、 in の代わりに使用するとwith authSnapletloginUserコードで型チェックを行うことができます。実際のインスタンスの制約を処理するには、少し調整する必要がある場合があります。


編集:上記の手法で何らかの方法で特定できない場合b、および型が実際に記述されているのと同じくらいジェネリックであることを意図していると仮定すると、bありえないほどあいまいであり、それを回避する方法はありません。

を使用すると、実際の型から完全にwith authSnaplet除外されますが、クラス制約が適用されたポリモーフィックなままになります。bこれは、式 like が持つのと同じ曖昧さでありshow . read、インスタンスに依存する動作を伴いますが、1 つを選択する方法はありません。

これを回避するには、おおよそ次の 3 つの選択肢があります。

  • あいまいな型を明示的に保持して、コンテキストだけでなくb、 の実際の型のどこかにあるようにします。loginUserこれは、アプリケーションのコンテキストでは、他の理由で望ましくない場合があります。

  • with authSnaplet適切に単相的な値にのみ適用して、多相性を取り除きます。タイプが事前にわかっている場合、あいまいさの余地はありません。これは潜在的に、ハンドラーのポリモーフィズムの一部を放棄することを意味しますが、物事を分割することで、モノモーフィズムを何bが重要なコードのみに限定することができます。

  • クラスの制約自体を明確にします。3 つの型パラメータ toHasAuthが、実際にはある程度相互依存していて、任意のsandに対して有効なインスタンスが 1 つだけ存在する場合u、他の to からの関数依存はb完全に適切です。

于 2011-12-17T17:36:15.207 に答える