2013-07-04 17 views
21

Haskellにはレコードメンバーのドット表記はありません。各レコードメンバーに対して、コンパイラーはRecType - > FieldType型の同じ名前の関数を作成します。これは名前の競合につながります。これを回避する方法はありますか?つまり、同じフィールド名を持つ複数のレコードを持つにはどうすればいいですか?Haskellレコードの名前の競合

+5

Cf. http://stackoverflow.com/questions/5775068/modeling-domain-data-in-haskell/5777042#5777042 – luqui

+0

http://stackoverflow.com/questions/6922437/ and: http://stackoverflow.com/questions/6677834/ –

+0

これはGHCのReal Soon Nowで修正されるかもしれません。私はそのGSoCプロジェクト:D – jozefg

答えて

14

この問題を回避する別の方法は、lensパッケージを使用することです。それは、あなたがこのように使用することができますmakeFieldsテンプレートHaskellの機能、提供しています。あなたはTemplateHaskellとレンズを使用しない場合

{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TemplateHaskell  #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
import   Control.Lens 

data A = A 
    { _aText :: String 
    } 
makeFields ''A -- Creates a lens x for each record accessor with the name _aX 

data B = B 
    { _bText :: Int 
    , _bValue :: Int 
    } 
-- Creates a lens x for each record accessor with the name _bX 
makeFields ''B 

main = do 
    let a = A "hello" 
    let b = B 42 1 

    -- (^.) is a function of lens which accesses a field (text) of some value (a) 
    putStrLn $ "Text of a: " ++ a ^. text 
    putStrLn $ "Text of b: " ++ show (b ^. text) 

を、あなたもTemplateHaskellを使用して自動化するどのようなレンズ手動で行うことができます。

{-# LANGUAGE FlexibleInstances  #-} 
{-# LANGUAGE FunctionalDependencies #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE TypeSynonymInstances #-} 
data A = A 
    { aText :: String 
    } 

data B = B 
    { bText :: Int 
    , bValue :: Int 
    } 

-- A class for types a that have a "text" field of type t 
class HasText a t | a -> t where 

    -- An accessor for the text value 
    text :: a -> t 

-- Make our two types instances of those 
instance HasText A String where text = aText 
instance HasText B Int where text = bText 

main = do 
    let a = A "hello" 
    let b = B 42 1 
    putStrLn $ "Text of a: " ++ text a 
    putStrLn $ "Text of b: " ++ show (text b) 

しかし、私はレンズの学習をお勧めします。これは、フィールドの変更や設定など、多くのユーティリティも提供しています。

+1

2つのフィールドの名前が同じで、タイプが異なる場合、これは機能しません。 –

+1

@ GabrielGonzalez少なくともレンズバージョンは、さまざまな種類の作品です。私は型のファミリまたは機能的な依存関係を持つマニュアルバージョンを複雑にしたくないので、異なる型のものを除外しました。 – bennofs

+1

実際、それは難しいことではないので、私はさまざまなタイプのサポートを追加しました。 – bennofs

17

大規模なプロジェクトでは、それぞれの型を独自のモジュールに保ち、それぞれの型の名前空間アクセサにHaskellのモジュールシステムを使用することをお勧めします。

例えば、私はいくつかのタイプのモジュールAAている可能性があります:

-- A.hs 

data A = A 
    { field1 :: String 
    , field2 :: Double 
    } 

...とモジュールBで同じ名前のフィールドを持つ別のタイプB:場合は、

-- B.hs 

data B = B 
    { field1 :: Char 
    , field2 :: Int 
    } 

を私は他のモジュールで両方の型を使いたいC私はそれらをインポートして、どのアクセサーを意味するのかを区別できます:

-- C.hs 
import A as A 
import B as B 

f :: A -> B -> (Double, Int) 
f a b = (A.field2 a, B.field2 b) 

残念ながら、Haskellは同じモジュール内に複数の名前空間を定義する方法がありません。そうしないと、各タイプを別のモジュールに分割してこれを行う必要があります。

+0

それは動作しますが、それは本当に醜いと冗長だと思う。 –

+3

これは非常に扱いにくいです、時々私はHaskellがネストされたモジュールをサポートしたいと思っています... – MathematicalOrchid

+2

@MathematicalOrchidそれはそれほど厄介ではありません。実際、このアプローチは他の言語では一般的です。たとえば、Javaでは、各タイプ(クラスまたはインタフェース)を別々のファイルに配置する必要があります。 –

6

GHC開発者は、今後この問題をどのように処理するか計画しているようです。 this blog postの最後に記載されている、this planを調べてください。

関連する問題