5

私は現在、Haskell で ADT をいじっていて、ADT を構築しようとしていFigureます:

data Figure = Rect { x :: Integer, y :: Integer, width :: Integer, height :: Integer}
            | Circle { x :: Integer, y :: Integer, radius :: Integer}
            | CombiFigure Figure Figure
            deriving (Eq, Show, Read)

Figure今、私は、すべてを受け入れるのではなく、たとえば a のみを受け入れる関数を実装する方法という質問に出くわしましたCircle

私はすでに悪いデザインを持っていますか?または、これを行うためのベストプラクティスはありますか?

例として、直径関数について考えてみましょう。私の頭に浮かんだのは (私は Haskell の完全な初心者です)、undefinedorを使用した次の 2 つのオプションだけMaybeです。

1:

diameter :: Figure -> Integer
diameter (Circle _ _ r) = 2 * r
diameter _ = undefined

2:

diameter :: Figure -> Maybe Integer
diameter (Circle _ _ r) = Just (2 * r)
diameter _ = Nothing

それを達成するためのより好ましい方法はありますか?ありがとう!

4

2 に答える 2

3

型定義自体 (つまりdata Figure = ...) は部分関数を導入しています。たとえばwidth、型width :: Figure -> Integerであっても、値に対してのみ機能しRectます。

\> width $ Rect 1 2 3 4
3
\> width $ Circle 1 2 3 
*** Exception: No match in record selector width

そのため、ある図では機能するが別の図では機能しない関数を既に定義しています(diameter質問の関数と同様)。

とはいえ、3 番目の解決策はCircleRectangleなどを別の型として定義することです。次に、これらの型の共通インターフェイスを定義するFigure 型クラスを定義します。

class Figure a where
    area, perimeter :: a -> Double

instance Figure Circle where
    area = ...
    perimeter = ...

さらに、各タイプには独自の専用機能がある場合があります。または、すべてではなく一部の Figure タイプをカバーするインターフェイス (つまり、タイプ クラス) を追加することもできます。

型クラスの利点は、拡張が容易なことです。たとえば、Triangle後で型を追加したい場合、三角形に適用される任意の型クラスを選択して、それらの型クラスのみのインスタンスを定義できます。

一方、アプローチでは、引数としてdata Figure = ...a を受け取ることができるすべての関数を見つけ、それが a も処理することを確認する必要があります。ライブラリを出荷している場合、これらすべての機能にアクセスすることはできません。FigureTriangle

>> 参考までに、最近haskell cafe メーリング リストでデータ宣言と型クラスの同様の議論がありました。

于 2016-01-23T14:30:56.820 に答える