2

次のデータ型が定義されているとします。

data X a = X {getX :: a}
data Y a = Y {getY :: a}
data Z a = Z {getZ :: a}

getXgetY、およびの 3 つの別個の関数が存在する必要がありますgetZか? 次のように定義された関数が存在する可能性があるように私には思えます。

get :: forall (τ :: (* -> *)) (a :: *). τ a -> a
get (_ x) = x

明らかに、これは有効な標準 Haskell ではありませんが、GHC には非常に多くの拡張機能があり、解決策があるようです ( RankNTypesExistentialQuantificationDataKindsなど)。少量のタイピングを回避するという単純な理由に加えて、レコード ソリューションによって作成される名前空間の汚染を回避するという利点があります。これは、次のような型クラスを使用するよりも、実際にはより暗黙的な解決策であると思います。

class Get f where
  get :: f a -> a

ただし、ジェネリック関数を定義する方が型クラスよりも有用であるように思われます。これは、暗黙的に定義されているという事実は、($)or(.)が使用されるのと同じ方法で、より多くの場所で使用できることを意味するためです。したがって、私の質問には 3 つの部分があります。これを達成する方法はありますか、それは良い考えですか、そうでない場合、より良い方法は何ですか?

4

2 に答える 2

10

このタイプはどうですか?

newtype Pred a = Pred (a -> Bool)

それともこれ?

data Proxy a = Proxy

から を取得する方法はありませaPred a。s を入れることしかできませんa。同様に、aa から を取り出す方法Proxy aはありませんa。その中に s がないからです。

したがって、関数get :: forall f a. f a -> aは一般に存在できません。f型クラスを使用して、抽出できる型と抽出できない型を区別する必要がaあります。

于 2016-10-27T21:35:36.133 に答える
3

まあ、その制約のないジェネリック型はget確かに機能しません。Voidこれにより、たとえばから値を抽出することもできますConst () :: Const () Void

ただし、ジェネリックを使用すると、この関数の適切に制約されたバージョンを非常に簡単に取得できます。型クラスは引き続き必要ですが、従来の意味でインスタンスを定義する必要はありません。最終的には次のようになります。

{-# LANGUAGE TypeFamilies, DeriveGeneric, DeriveAnyClass #-}
import GHC.Generics

class Get τ where
  get :: τ a -> a

data X a = X a deriving (Generic1, Get)
data Y a = Y a deriving (Generic1, Get)
data Z a = Z a deriving (Generic1, Get)

これを実際に機能させるために必要なのは、2 つの奇妙な表現型のインスタンスだけです。

instance Get f => Get (M1 i t f) where get = get . unM1
instance Get Par1 where get = unPar1

の実際の実装ではX、デフォルトの署名を使用して、抽出YZ基になる型表現に減らすことができます。このために、次のようにクラスを定義します。

{-# LANGUAGE DefaultSignatures #-}

class Get τ where
  get :: τ a -> a
  default get :: (Generic1 τ, Get (Rep1 τ)) => τ a -> a
  get = get . from1
于 2016-10-27T22:46:22.223 に答える