2

Snap Frameworkでは、Snapletを使用して、コンポーネントベースのインターフェイスを介して他のSnapletに機能を埋め込みます。メインのWebアプリケーションは、従来の「has-a」関係を介して他のSnapletを参照するSnapletであり、サブSnapletは順番に参照できます。他のスナップレット。

さまざまなSnapletの実装を見ると、Snapletを親Snapletに埋め込むためにさまざまなパターンが使用されているのがわかりました。具体的には:

  • 参照の種類。Snapletの実装は、親Snapletとの特定の種類の関係が存在することを前提としています。これは、使用されるReferenceメソッドを介して実施されます(以下を参照)。

    1. わかりやすいリファレンス:

      data MySnaplet = MySnaplet { subSnaplet :: Snaplet SubSnaplet }
      
    2. 相対レンズ:

      data MySnaplet = MySnaplet { _subSnaplet :: Snaplet SubSnaplet }
      
      subSnaplet :: Lens MySnaplet SubSnaplet
      subSnaplet = lens _subSnaplet $ \ a b -> a { _subSnaplet = b }
      
  • 参照方法。Snaplet実装は、そのインターフェースを介して、Snapletデータにアクセスする特定の方法が実施され、さまざまなSnaplet実装がさまざまなメソッドを使用することを強制します。Snapletは、次のことを前提としています。

    1. データはMonadState、Snapletを操作する関数が呼び出されるたびに存在します。
    2. データはに存在し、ラッパーMonadStateでラップされます。Snaplet
    3. 関数を呼び出す時点でaがainstance HasSubSnaplet MySnapletにある場合、Snapletデータを取得する関数を持つそのようなクラス+インスタンスがあります。MySnapletMySnapletMonadState
    4. 3.の関数はMySnaplet -> Snaplet SubSnaplet代わりにタイプを持っています。
    5. 3.のようなclass+instanceがあります。これは。を提供しますLens MySnaplet (Snaplet SubSnaplet)
    6. class+instanceには。が必要Lens (Snaplet MySnaplet) (Snaplet SubSnaplet)です。
    7. class + instanceは、それMySnapletがアプリケーションの「トップスナップレット」であると想定し、絶対レンズ/参照を必要とします。これは、に含まれているMySnaplet必要があります。bMonadSnaplet

私が見ているように、参照の種類1.スナップレットが読み取り専用の場合は意味があり、2。スナップレットを変更する必要がある場合は意味があります。

さらに、メソッドのクラスを持つMySnapletことは、1つしか持てない場合に意味SubSnapletがあり、絶対参照を持つことは、データベースなど、コンポーネントとして構成できない可能性があるものに意味がある場合があります。これは、最上位のSnapletのみがアクセスできるためです。クレデンシャルとそうでないもの。ただし、Snapletライターとしてこの仮定を行うことは誤りである可能性があり、代わりに相対参照を使用することに不利な点はありません。

ただし、問題が1つあります。ハッキングに関する既存のスナップレットは、私が行ったこれらの仮定に適合しません。上記のすべての方法は、一見ランダムに、あらゆる種類の状況で使用されます。また、上記の他のいくつかの側面(Snapletラッパーが必要かどうかなど)に長所/短所はありません。

私には、種類2を参照し、方法1、2、5、または6のいずれかがすべての状況で最も理にかなっているように見えます。また、たとえば(2、1)だけを常に使用することにコンセンサスがない理由はわかりません。 。

それで:

Snapletライターとして、新しいSnapletを作成するときにどの方法を使用するか(汎用性があると想定)、および

存在するすべてのSnapletがまだ同じ参照メソッドを使用していない理由は何ですか(コアsnapパッケージでも、多数の異なるメソッドが使用されています)?

4

1 に答える 1

1

TLDR; withほとんどの場合、関数レンズと相対レンズを使用したいと思うでしょう。

HasSubSnaplet 型クラスの使用は完全にオプションのパターンであり、SubSnaplet の複数のインスタンスを持つことが意味をなさない状況で、「with subSnaplet」ボイラープレートを減らすことができます。Snap パッケージに同梱されている Heist スナップレットでこれを選択したのは、Heist スナップレットにとって意味があり、ユーザーにパターンの例を提供するためです。

型クラスは完全にオプションであり、レンズの選択とほぼ直交しているため、この回答の残りの部分では、型クラスなしで何をすべきかに焦点を当てます。

API の目的は、レンズ (Snaplet データ型でラップされた何かを取得する「プレーンな参照」ではない) を使用して状態にアクセスすることです。これは、リクエスト処理中に状態を変更することが、Snaplets に提供してほしい基本的な機能であるためです。Snapletほとんどの場合、エンド ユーザーがスナップレットの状態タイプを除いて触れる必要のない不透明なラッパーになることを意図しています。これが、MonadState インスタンスが Snaplet ラッパーなしで型に直接アクセスできる理由です。

とはいえ、4 つの基本的なアクセス パターンがあります。これらはMonadSnaplet 型 classを見ればわかります。

with     :: Lens     v       (Snaplet v') -> m b v' a -> m b v a
withTop  :: Lens     b       (Snaplet v') -> m b v' a -> m b v a
with'    :: Lens (Snaplet v) (Snaplet v') -> m b v' a -> m b v a
withTop' :: Lens (Snaplet b) (Snaplet v') -> m b v' a -> m b v a

最初の 2 つの関数で具現化されたレンズ パターンは、TemplateHaskell を含む data-lens-template パッケージによって自動的に生成される種類のレンズであり、最も自然に使用できます。したがって、これは推奨されるパターンです。with(したがって、短い、プライムされていない名前です。)との違いwithTopwith、相対レンズを使用しwithTop、絶対レンズを使用することです。ほとんどの場合、相対レンズを使用します。しかし、絶対レンズの使用を許可したかったのは、あるスナップレットが別のスナップレットによって提供される何かを取得する必要があるかもしれないが、現在のスナップレットの子孫ではない複雑なアプリケーションを想像できるからです。

時折、アイデンティティレンズを持ちたいと思う状況があります。それにはLens (Snaplet a) (Snaplet b). したがって、次の 2 つの素数付き関数は、この種のレンズを使用することを除いて、最初の 2 つに類似しています。

于 2012-01-20T17:00:48.643 に答える