5

次の 2 つのタイプを検討してください。

data Point=Point{x::Float,y::Float}
data Rectangle = {upperLeft::Point, bottomRight::Point}
data Square = {upperLeft::Point, bottomRight::Point}

ghc コンパイラは、Rectangle の upperLeft フィールド名が Square のフィールド名と競合していると文句を言います。各フィールド名は型の名前空間にある必要があるため、これは奇妙に思えます。そうしないと、フィールド名を再利用できません。それは十分に一般的な期待であると思います。

たとえば、変数を定義するには、次のように記述します。

let a=Rectangle{upperLeft=Point 2 3, bottomRight=Point 7 7}
let a=Square{upperLeft=Point 2 3, bottomRight=Point 7 7}

このことから、各フィールド名がそれぞれの型の名前空間内にあることが期待できるはずであることがわかります。

私の使用法は正しいですか、それとも私の期待は間違っていますか? これを修正する方法はありますか?

4

3 に答える 3

4

オブジェクトはフィールド名からアクセスできるため、コンパイラはフィールド名からオブジェクトの型を推測できる必要があります。たとえば、

boundingBox x = bottomRight x - upperLeft x

アクセサーbottomRightupperLeftの型を推測するために使用されxます。複数の型が同じアクセサー名を持つことが許可されている場合、型を推測することはできません。

名前の衝突を避けるために、すべてのフィールド名に接頭辞を付けるのが一般的な規則です。この規則は GHC プロジェクトで使用されます。

data Rectangle = {rc_upperLeft :: Point, rc_bottomRight :: Point}
data Square =    {sq_upperLeft :: Point, sq_bottomRight :: Point}
于 2013-01-10T21:04:33.347 に答える
3

Haskell では、レコード型を作成すると、アクセサー関数も作成されます。たとえば、x (upperLeft a)上記で定義した変数のいずれかで次を実行して 2 を取得できます。レコード構文の概要は次のとおりです。

オプションは、レコードで異なるフィールド名を使用するか、それらを別々のモジュールに入れることです。モジュールにはそれぞれ独自の名前空間があるため、Square モジュールに Square を配置し、Rectangle モジュールに Rectangle を配置すると、フィールド名を再利用できます。

于 2013-01-09T22:54:06.407 に答える
2

Square コンストラクターを Rectangle 型に配置することで回避できる場合があります。これは、実際にはその型の特殊化であるためです。

GHCIで何かを入力すると、これはうまくいくようです:

data Point
    = Point{x::Float,y::Float}
    deriving (Eq, Show)

data Rectangle
    = Rectangle {upperLeft::Point, bottomRight::Point}
    | Square    {upperLeft::Point, bottomRight::Point}
    deriving (Eq, Show)

let r = Rectangle (Point 3.0 4.0) (Point 4.0 2.0)
let s = Square (Point 2.0 4.0) (Point 4.0 2.0)

次に、両方を呼び出すことができます。

upperLeft s
upperLeft r

満たす必要があるいくつかの制約があるため、Square を変更したい場合があります。

data Rectangle
    = Rectangle {upperLeft::Point, bottomRight::Point}
    | Square    {upperLeft::Point, size::Float}
    deriving (Eq, Show)
于 2013-01-10T18:52:27.853 に答える