3

モジュールに操作を抽象化させAreaます (悪い定義)

class Area someShapeType where
  area :: someShapeType -> Float

-- module utilities
sumAreas :: Area someShapeType => [someShapeType]
sumAreas = sum . map area

事後的な明示的な形状型モジュールを作成します (適切または許容可能な定義)

data Point = Point Float Float

data Circle = Circle Point Float
instance Surface Circle where
  surface (Circle _ r) = 2 * pi * r

data Rectangle = Rectangle Point Point
instance Surface Rectangle where
  surface (Rectangle (Point x1 y1) (Point x2 y2)) = abs $ (x2 - x1) * (y2 - y1)

いくつかのデータをみましょう

c1 = Circle (Point 0 0) 1
r1 = Rectangle (Point 0 0) (Point 1 1)

次に、使用しようと

totalArea = sumAreas [c1, r1]

[c1, r1]タイプは or ! に展開する必要があり[Circle]ます[Rectangle]。(そして有効ではありません)

私はこのような追加のタイプを使用forall して行うことができますdata

data Shape = forall a . Surface a => Shape a

sumSurfaces :: [Shape] -> Float
sumSurfaces = sum . map (\(Shape x) -> surface x)

次に、次のコードが正常に実行されます

sumSurfaces [Shape c1, Shape r1]

しかし、data ShapeandShapeコンストラクター (on[Shape c1, ...]と lambda 引数) の使用は見苦しいと思います (私の最初の [そして悪い] 方法はきれいです)。

「Haskellでの異種多型」を行う正しい方法は何ですか?

お時間をいただきありがとうございました!

4

3 に答える 3

8

あなたの最初の(そして悪い)方法はきれいではありません、それはLispyです。これは、静的に型付けされた言語では不可能です。たとえばJavaでそのようなことを行う場合でも、実際には、に類似した基本クラスのポインタを使用して、個別の定量化ステップを導入していdata Shape = forall a. Surface aます。

存在記号が良いかどうかについては論争がありますが、ほとんどのハスケラーはそれをあまり好きではないと思います。ここで使用するのは確かに正しいことではありません。sum [ area c1, area c2 ]はるかに簡単で、同様に機能します。しかし、見た目が異なる、より複雑な問題が確かにあります。異種のポリモーフィズムが「必要」な場合は、実存主義が進むべき道です。

Haskellは怠惰なので、すべての可能な操作(この例ではareaそれだけ)を「先制的に」適用し、すべての結果をいくつかのレコードに保存し、これらのレコードのリストを出力することができます。多態的なオブジェクトのリストの代わりに。このようにして、すべての情報を保持します。

または、それはもっと慣用的なことですが、そのようなオブジェクトのリストをまったく作成しないでください。オブジェクトを使って何かをしたいので、これらのアクションを、さまざまなを生成する関数に渡しShapeて、適切な場所に適用してみませんか。この逆転は、存在記号を全称記号と交換しますが、これはかなり広く受け入れられています。

于 2012-12-02T18:56:04.267 に答える
5

あなたが最初にしたことは、実存的なアンチパターンにぶつかることです。

とにかくここでクラスを使用するのはなぜですか?

data Shape = Shape { area :: Double }

data Point = Point Double Double

circle :: Point -> Double -> Shape
circle p r =
    Shape $ 2 * pi * r

rectangle :: Point -> Point -> Shape
rectangle (Point x1 y1) (Point x2 y2) =
    Shape $ abs $ (x2 - x1) * (y2 - y1)

これで、必要なもの (形状のリスト) を簡単に取得できます。

*Main> map area [circle (Point 2 0) 5, rectangle (Point 0 0) (Point 2 10)]
[31.41592653589793,20.0]
于 2012-12-02T23:08:03.043 に答える