1

私は Haskell を学んでいますが、Haskell は非常にエレガントで強力です。しかし、OOP を使用して非常に単純に見えることを行う方法をイメージするのはまだ困難です。たとえば、zope3/grok/pyramid Web プラットフォームを取り上げます。それらには、コンテンツとビューを一致させてページをレンダリングすることに基づく美しい哲学があります。

Zope では、異端のコンテンツ タイプのツリーがあります。URL を要求すると、traverseそのツリーへのパスを使用してcontextオブジェクトを取得します。パスの最後の部分は「ビュー名」です。Viewオブジェクトのタイプとビュー名に基づいて取得します。はView特定のコンテキスト タイプからオブジェクトRequestへの関数です。Response

したがって、URL http://example.com/aFolder/aFaq/aQuestion/index.htmlにアクセスすると、zope はツリーのルートから開始されます。という名前のノードを検索し、aFolderこのオブジェクト内で別の という名前のノードを検索し、その内部でaFaqという名前のノードを検索しますaQuestion。したがって、traverse関数は任意の型のオブジェクトを返すことができます。

ここは問題ありません。Traversableツリーをトラバースしているだけなので、 Haskell で という名前の新しいラッパー タイプまたはクラスを作成できTree Traversableますtraverse :: Tree Traversable -> [string] -> Traversable

しかし、問題index.htmlは、ビューの名前です。簡単に説明すると [*] Zope はペア (コンテキストの型、ビュー名) を探し、大まかに言えば型の関数を返します{type of the context} -> Request -> Response。traversable の型をチェックする function を書くことはできますrender :: Traversable -> String -> Responseが、誰かが新しいコンテンツ タイプまたは新しいビューを追加するたびに、その関数を更新する必要があります。ビュー関数 (またはサブ関数) は、そのデータを使用するためにコンテキストのタイプを知る必要があります。

では、ベテランの haskeller はこの種の問題にどのように対処するのでしょうか? しばらく GADT で考えましたが、それが役立つかどうか、またはもっと簡単な代替手段があるかどうかはわかりません。

ありがとう!

編集:明確にするための疑似コード

def traverse(node, path):
    # returns the context and the view name
    itemname = path[0]
    if hasattr(node, itemname):
       # The next element in the path is a subnode of the node, let's visit it
       return traverse(node[itemname], path[1:])
    else:
       # We can't go down the tree anymore, we found our context and view name
       viewname = itemname
       return node, viewname

def render(tree, request):
    path = somehow_get_path_from_request(request)
    context, viewname = traverse(tree, path)
    # We get the view from a registry which is a map/dictionary
    view = registry[(context, viewname)]
    # here comes the problem:
    #   view is an object that knows exactly the type of context
    #   A view for a Question object can use its 'question' and 'answer' fields
    #   A View for a Folder can use its 'items' fields, a view for Image can use
    #   its 'img' field.
    #
    return view.render(context, request)

これは、haskell で行う方法がわからないことです。Haskell f にはツリーがあり、同種のオブジェクトでなければなりません。そのため、ラッパー タイプ Traversable を定義する必要があります。しかし、誰かが新しい型を追加したい場合は、私のコードを変更する必要があります。または、Haskell クラス Traversable を作成することもできます。このようにして、将来の型をツリーに追加できます。しかし、(context, viewname) から不明なコンテキストの関数にマップするにはどうすればよいでしょうか?

[*] 真実はもう少し複雑です。zope では、オブジェクトまたはそのクラスは実行時に任意のマークを付けることができますinterfaces(python にはインターフェースの概念がありません。これは完全に zope の構築です)。インターフェイスはツリーを形成します。ビューをペア (インターフェース、名前) に関連付けます。(コンテキスト、名前) のビューを要求すると、最も具体的なインターフェイスに関連付けられたビューが返されます。アイデアは、コードを変更せずに、インターフェイスのレジストリを変更してビューを変更できるということです。

4

2 に答える 2

5

私がこの権利を理解しているなら、あなたの問題は「未知のタイプのものをコンテナに詰め込み、実行時にそれらを引き出して、それらのタイプに基づいて異なる関数を実行したい」に要約されます。

複雑さの程度はさまざまですが、この問題を攻撃する方法はいくつかあります。

まず第一に、Haskellには型消去があります。プログラムをコンパイルするとき、コンパイラはすべてのタイプに問題がないことを確認した後、それらを消去します。したがって、実行時に、何かがどのタイプであるかを判断することは不可能です。これが機能するのは、コンパイル時に、何かのタイプを知ることが不可能だからです。これは、「Haskellは静的に型付けされている」という冗長な言い方です。

とは言っても、これを行うには「いくつかの拡張機能を備えたHaskell」を作成できます。しかし、私はあなたの問題がはるかに簡単な解決策を持っているかもしれないと思います...

さて、あなたはさまざまな種類のものを保存したいと思います。しかし、保存したら、実際にこのようなものに何をしたいですか?データに対して(そのタイプに応じて)関数を実行したいだけの場合は、関数自体を保存してみませんか?

考えてみてください。内部にさまざまなタイプがたくさんあるコンテナの代わりに、同じタイプの関数がたくさん含まれているだけです。必要な関数をフェッチして実行します。終わり。

あれは何でしょう?データに対していくつかの異なる関数を実行できる必要がありますか?その場合は、必要なすべての関数を含むデータ構造を保存してみてください。

于 2012-07-09T09:33:06.553 に答える
3

したがって、具体化された Typeclass が役立つと思います。あなたが何を望んでいるのか、私にはまだよくわかりません。

{-# LANGUAGE ExistentialQuantification #-}

data Showable = forall a. (Show a) => Showable a

instance Show Showable where
  show (Showable x) = show x

exampleList = [Showable 42, Showable "have a nice day"]

あなたの場合、Context 型のクラスを作成してから、その上にデータ型を作成し、それを Contextable と呼ぶ必要があります (恐ろしい名前ですが、現時点ではもっと良いものは思いつきません)。
次に、Data.Map を (Contextable, ViewName) から Contextable -> Request -> Response に保存できます。

ここで、関連付けられたビューに必要な情報が含まれている SpecificContext があるが、他のコンテキストにはそれがない場合は、次のようなことができます

class Context a where
    maybeSpecificContext :: a -> MaybeSpecificContext
    ...

instance Context SpecificContext where
    maybeSpecificContext ctx = Just ctx
    ...

instance Context OtherContext where
    maybeSpecificContext _   = Nothing
    ...

しかし、それは少し見栄えが悪いと思います。おそらく、コンテキストの属性に対してこれを行うので、重複した場合の作業が少なくなります。しかし、それはうまくいくでしょう。

于 2012-07-07T18:46:00.660 に答える