15

私は Haskell の初心者です。Haskell がレコード名のオーバーロードをサポートしていないことに気付きました:

-- Records.hs

data Employee = Employee
  { firstName :: String
  , lastName :: String
  , ssn :: String
  } deriving (Show, Eq)

data Manager = Manager
  { firstName :: String
  , lastName :: String
  , ssn :: String
  , subordinates :: [Employee]
  } deriving (Show, Eq)

これをコンパイルすると、次のようになります。

[1 of 1] Compiling Main             ( Records.hs, Records.o )

Records.hs:10:5:
    Multiple declarations of `firstName'
    Declared at: Records.hs:4:5
                 Records.hs:10:5

Records.hs:11:5:
    Multiple declarations of `lastName'
    Declared at: Records.hs:5:5
                 Records.hs:11:5

Records.hs:12:5:
    Multiple declarations of `ssn'
    Declared at: Records.hs:6:5
                 Records.hs:12:5

Haskell 型システムの「強さ」を考えると、どのフィールドにアクセスするかをコンパイラが簡単に判断できるように思われます。

emp = Employee "Joe" "Smith" "111-22-3333"
man = Manager "Mary" "Jones" "333-22-1111" [emp]
firstName man
firstName emp

私が見ていない問題はありますか?Haskell レポートがこれを許可していないことは知っていますが、なぜ許可しないのでしょうか?

4

3 に答える 3

16

歴史的な理由。Haskell の記録システムを改善するために、多く競合する設計 ありました。まだ。

于 2012-06-19T11:56:17.180 に答える
9

現在の記録システムはあまり洗練されていません。ほとんどの場合、レコード構文がなかった場合にボイラープレートでできることのための構文糖衣です。

特に、これは:

data Employee = Employee
  { firstName :: String
  , lastName :: String
  , ssn :: String
  } deriving (Show, Eq)

(とりわけ) function を生成しますfirstName :: Employee -> String

同じモジュールでこのタイプも許可する場合:

data Manager = Manager
  { firstName :: String
  , lastName :: String
  , ssn :: String
  , subordinates :: [Employee]
  } deriving (Show, Eq)

次に、firstName関数の型は何でしょうか?

Haskell では許可されていない、同じ名前をオーバーロードする 2 つの別個の関数でなければなりません。これが暗黙的に型クラスを生成し、名前付きのフィールドを持つすべてのものに対してそのインスタンスを作成することを想像しない限りfirstName(フィールドが異なる型を持つ可能性がある場合、一般的なケースでは混乱します)、Haskell の現在のレコード システムは機能しません。同じモジュールで同じ名前の複数のフィールドをサポートします。Haskell は現在、そのようなことをしようとさえしていません。

もちろん、もっとうまくやれるかもしれません。しかし、解決しなければならない厄介な問題がいくつかあり、基本的に、最も有望な方向性があることを誰もが確信できるような解決策を思いついた人はいません。

于 2012-06-19T12:18:04.437 に答える
4

これを回避する1つのオプションは、データ型を異なるモジュールに配置し、修飾されたインポートを使用することです。このようにして、異なるデータレコードで同じフィールドアクセサーを使用し、コードをクリーンで読みやすくすることができます。

たとえば、従業員用に1つのモジュールを作成できます

module Model.Employee where

data Employee = Employee
  { firstName :: String
  , lastName :: String
  , ssn :: String
  } deriving (Show, Eq)

そして、マネージャー用の1つのモジュール、たとえば:

module Model.Manager where

import Model.Employee (Employee)

data Manager = Manager
  { firstName :: String
  , lastName :: String
  , ssn :: String
  , subordinates :: [Employee]
  } deriving (Show, Eq)

そして、これら2つのデータ型を使用する場合はいつでも、修飾されたものをインポートして、次のようにアクセスできます。

import           Model.Employee (Employee)
import qualified Model.Employee as Employee
import           Model.Manager (Manager)
import qualified Model.Manager as Manager

emp = Employee "Joe" "Smith" "111-22-3333"
man = Manager "Mary" "Jones" "333-22-1111" [emp]

name1 = Manager.firstName man
name2 = Employee.firstName emp

結局のところ、2つの異なるデータ型を使用しているため、両方のデータ型が人を表し、各人が名を持っていることがわかっている場合でも、Manger.firstNameはEmployee.firstNameとは別の関数であることに注意してください。ただし、たとえば、これらの「属性コレクション」からPersonデータ型を作成する場合など、抽象データ型にどこまで進むかはあなた次第です。

于 2012-07-07T12:50:38.283 に答える