2016-10-27 8 views
2

次のデータ型が定義されていると仮定します。より高い種類の関数?

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

は、3つの別々の機能、getXgetY、およびgetZが存在しなければなりませんか?このような何か定義された関数があるかもしれないように私には思える:

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

明らかにこれは有効な標準Haskellのではなく、彼らは解決策を持っている可能性があるように見えるGHCに非常に多くの拡張機能(RankNTypesExistentialQuantificationがあります、DataKindsなど)。わずかな量の入力を避ける簡単な理由の他に、レコードソリューションが作成する名前空間の汚染を避けるという利点があります。私は、これは本当にこのように型クラスを使用するよりもちょうどより多くの暗黙的な解決策であると仮定します

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

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

+0

'get'関数がリストを与えられた場合(つまり、'τ'が '[]'だった場合)はどうなりますか? – duplode

+3

まあ、それはうまくいかないことを暗黙の前提としています。kind *という最終的な型変数を持つ型は、問題の型の単一の引数を取る単一のコンストラクタを持つと仮定する理由はありません。 – Carl

答えて

10

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

newtype Pred a = Pred (a -> Bool) 

またはこれ?

data Proxy a = Proxy 

Pred aaアウトを取得する方法はありません。 aのみを入力することができます。同様に、aが内部にないため、Proxy aからaを取得する方法はありません。

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

3

まあ、その無制限のジェネリックタイプgetは確かに動作しません。これにより、たとえばVoidの値をConst() :: Const() Voidから抽出することもできます。

しかしながら、この関数の適切に制約されたバージョンをgenericsと非常に簡単に得ることができます。依然として型クラスが必要ですが、従来の意味でインスタンスを定義する必要はありません。それは最終的に次のようになります。今XYZの実際の実装は、単に使用することができます

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

:実際にこの作業を取得するには

{-# 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つだけ奇妙な表現型のインスタンスを必要としますデフォルトのシグネチャを生成し、抽出を基になる型表現に縮小します。この目的のために、クラスを次のように定義します。

{-# LANGUAGE DefaultSignatures #-} 

class Get τ where 
    get :: τ a -> a 
    default get :: (Generic1 τ, Get (Rep1 τ)) => τ a -> a 
    get = get . from1