2016-11-05 7 views
4

私はHaskellを学習しており、現在データメンバーのための「アクセサ」を発見しています。 のは、私はいくつかのダミー2D頂点情報、いくつかの色を持っている1種類、いくつかのテクスチャ座標を持っている以外の(TC)があると仮定してみましょう:存在しないレコードのHaskellアクセサリー

data SVertex = VertexC (Float, Float) Int 
     | VertexTC (Float, Float) (Float, Float) 
     deriving(Show) 

一つの退屈な方法レコードをアクセサを作成するためには、パターンを持つ関数を書くことです:

position (VertexC (x,y) c) = (x,y) 
position (VertexTC (x,y) c) = (x,y) 
tc (VertexTC _ tc) = tc 
color :: SVertex -> Int 
color (VertexC _ c) = c 

さて、正の機能は、私が「色」や「TC」を持っていないもののためにアクセサ(「色」と「TC」)を追加することができていることである:

position (VertexC (x,y) c) = (x,y) 
position (VertexTC (x,y) c) = (x,y) -- no header, here... still works 
tc (VertexTC _ tc) = tc 
tc (VertexC _ _) = (0,0) -- to returns something even if the field doesn't exist 
color :: SVertex -> Int 
color (VertexC _ c) = c 
color (VertexTC _ _) = 0 -- return something even if field doesn't exist 

テクスチャ座標を持たない頂点、または色を持たない頂点に0のデフォルト値を与えることができます。 すべて良い...

今、私の質問私は現在、データ宣言にアクセサー名を付ける良い方法があると読んでいます。ここに私の場合は は、私は(名前の競合を避けるために、「プライム」を使用して)になるだろうです:「位置「」、「TC」」と「色:

data SVertex' = VertexC' { 
    position' :: (Float, Float), 
    color'  :: Int 
    } 
    | VertexTC' { 
    position' :: (Float, Float), 
    tc'   :: (Float, Float) 
    } deriving(Show) 

これは私が同じ目標を達成することができます''アクセサが私のために作成されました!

:存在しないフィールドにデフォルトアクセサーを付ける方法が見つかりませんでした。たとえば、 'VertexC'でtcを要求するとき。またはVertexTCの色を要求しています... 最初のアプローチでは、私はそれを実現させることができます。この便利な第二のアプローチでは、私はそれが不可能であることを恐れている。 私は

color' (VertexTC' _ _) = 0 

のような他の機能パターンを追加しようとすると、コンパイラは私に語っ「などの 『色』の複数の宣言」。そして、それは、この2番目の宣言がコンパイラによって作成された前の暗黙のものに続いて行われないためです。

回避策をご存知ですか?

+2

私はこれが可能ではないと思います。Haskellのレコードは、一般的には苦労しています(特に、部分的であるという事実は、あなたが取得しているもののようなものです)。レコードに興味があるなら、おそらく、 '' lens''(https://hackage.haskell.org/package/lens)をある時点でチェックアウトすることになります(この部分的性は 'Prism'抽象化を使って扱われます) )。 – Alec

+1

これは不可能ですが、非慣用的なHaskellの指標にもなります。グローバルルールを作るのではなく、 'SVertex'インスタンスを使ってこれらのデフォルトを扱うときに、パターンマッチングをより重視することを検討してください(後者のアプローチは非常にJava風です)。 –

+0

私はHaskellで「レコード」にアクセスできないことに気付かなかった。どのように私はこのことに気づくべきであるかについての確認をありがとう。 この範囲で改善を図っている人もいるようです:http://nikita-volkov.github.io/record/ –

答えて

4

レコードは、あなたが取り除くことができない不愉快な部分アクセサーにつながるので、レコードは合計型(すなわち、複数のコンストラクターを持つ型)とよく混合しません。 1つの代替方法は、SVertexを全体の合計型として作るのではなく、実際にそれが必要なフィールドに対してのみ和タイプを使用することです。そうすれば、部分的なものを避けながら、できるだけ多くの素晴らしいアクセサーを得ることができます。

data VertexPaint = VertexC Int | VertexTC (Float, Float) 
    deriving (Show) 

data SVertex = SVertex 
    { position :: (Float, Float) 
    , paintjob :: VertexPaint 
    } deriving (Show) 

あなたがcolor機能が必要な場合、あなたはまだ最初の試みのように、個別に定義する必要があります。 (それは少なからず、任意のデフォルトを返すよりも安全賭けであるとしてここで私は、Maybe Int結果が使用されます。)

color :: SVertex -> Maybe Int 
color v = case paintjob v of 
    VertexC c -> Just c 
    VertexTC _ -> Nothing 

アレックが示すように、lensライブラリは、この種に対処するためのツールの多くを提供していより便利な状況での状況。いずれにしても、この答えに定義されているタイプは、レンズでうまくいくでしょう。

関連する問題