1

Shapeすべての形状に共通するいくつかの関数を宣言する型クラスがあります。これらの関数の1つ(refine)は、サブシェイプのリストを返す必要があります。この制約を表現するために、存在記号を使用します。

data Shapeable = forall a . Shape a => Shapeable a

関数returnを持っています[Shapeable]。一部の形状は(関数を介して)リファインでき、他の形状は(関数を介しrefineて)交差をチェックできるという追加の制約がありintersectます。これらは相互に排他的であり、それ自体を洗練できる形状は交差をチェックできず、その逆も同様です。

数量化を使用していなかった場合は、さらに2つの型クラスを作成したことにIntersectableなりRefineableます。システムのような単一の型クラス内で互いに素な関数セットを表現する方法はありますか?

4

3 に答える 3

5

私はこのようなものを提案します:

data Shape
    = Composite
        { refine :: [Shape]
        , {- other type-class methods go here -}
        }
    | Primitive
        { intersect :: Shape -> Region
        , {- other type-class methods go here -}
        }

...そして型クラスと存在記号を完全にスキップします。

于 2012-07-17T18:42:53.740 に答える
2

私はあなたが得ることができる最も近いものは2つの実存的なケースを持つことであると信じています:

data Shapeable =
    forall a . (Shape a, Intersectable a) => Intersectable a |
    forall a . (Shape a, Refineable a) => Refineable a
于 2012-07-17T19:24:04.970 に答える
1

型クラスをまったく使用しないことをお勧めします。操作を単純なデータ型として定義します。

data ShapeOps a =
    ShapeOps {
      intersect :: Maybe (a -> a),
      refine    :: Maybe (a -> a)
    }

次に、存在記号を使用できます。

data Shape =
    forall a. Shape (ShapeOps a) a

この概念は、因数分解がはるかに簡単です。

data Shape =
    forall a.
    Shape {
      intersect :: Maybe (a -> a),
      refine    :: Maybe (a -> a),
      shape     :: a
    }

使用Maybeは一例にすぎません。存在記号の代わりにRankNTypesを使用することもできます。

newShape ::
    (forall a. (a -> a) -> a -> b) ->
    (forall a. (a -> a) -> a -> b) ->
    ShapeConfig ->
    b

この関数は、交差がある場合は最初の継続に、洗練されている場合は2番目の継続に形状を渡すことができます。組み合わせるためのあらゆる種類の方法を考えることができます。Monoidまたはを使用Alternativeして、両方を実行することもできます。

newShape ::
    (Alternative f) =>
    (forall a. (a -> a) -> a -> f b) ->
    (forall a. (a -> a) -> a -> f b) ->
    ShapeConfig ->
    f b

RankNTypesを使用すると、より柔軟な関数を記述できるという利点があります。単純なコンストラクター関数の代わりに、フォールド、マップ、または任意のものを使用できるようになりました。

于 2012-07-17T23:29:17.793 に答える