2012-06-19 9 views
15

私はハスケル初心者です。私は私が得るこれをコンパイルするとなぜHaskell/GHCはレコード名のオーバーロードをサポートしていません

-- 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) 

::私はHaskellはレコード名のオーバーロードをサポートしていないことに気づいた、それがあるべきよう

[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 

私には見えない問題がありますか?私はハスケル・レポートがこれを許さないことを知っていますが、なぜそうではありませんか?

+1

これはまったくあなたの質問に対する答えではありませんが、あなたのような状況が発生するたびにデータ型を別々のモジュールに分割します。たとえば、 'Employee'モジュールと' Manager'モジュールを作成し、それぞれ 'E'と' M'のように修飾し、 'E.firstName'、' M.firstName'などを使用しますこれは私に合理的にいい構文を与えます。 (私はこれが必ずしも良いアイデアだと言っているわけではありませんが、それは私がやり遂げたことであり、私の場合はうまくいきました)。 – gspr

+3

ええ、これはそうでなければエレガントな言葉では "kludge"のようです。 – Ralph

答えて

15

歴史的な理由。ハスケルのためのよりよい記録システムのためにhave beenmany competing designsがあります - 実際には多くのことが、no consensusに達する可能性があります。それでも。

9

現在のレコードシステムはあまり洗練されていません。レコード構文がなければ、定型文を使ってできることのほとんどは構文的な砂糖です。特に

、これ:

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

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

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

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

その後、何がfirstName機能のタイプでしょうか?

ハスケルでは許可されていない同じ名前の2つの別々の関数をオーバーロードする必要があります。暗黙のうちにこれが暗黙的にtypeclassを生成し、firstNameという名前のフィールド(すべてのフィールドが異なるタイプを持つことができる一般的なケースでは乱雑になります)ですべてのインスタンスを作成すると仮定しない限り、Haskellの現在のレコードシステムは同じモジュール内の同じ名前の複数のフィールドをサポートすることができます。ハスケルは現在そのようなことをやろうとしません。

これは、もちろん、よりうまくいく可能性があります。しかし、解決すべきいくつかのトリッキーな問題があり、本質的に誰もが移動する最も有望な方向性があることを誰にも納得させる解決策を思いついてはいません。

+0

typeclassを作成して、特定のレコードバージョンをtypeclassメソッドで呼び出すことができますが、通常はそれほど必要ではない言語に定型文を追加します。 – Ralph

+1

フィールドのオーバーロードを許可した場合、 'firstName'関数は' forall a型でなければなりません。 a'。型推論または明示型宣言では、この型は特殊化されている必要があります。 Agdaのレコードコンストラクタは、このように動作します。 – JJJ

4

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

あなたは、たとえば、たとえば

module Model.Employee where 

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

とマネージャーのための1つのモジュールのために、従業員のための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データ型を作成するなど、あなたがどれだけ遠くにいるかによって決まります。

関連する問題