2012-08-24 15 views
7

時々、現存量の型の値を返す必要が生じます。これは、私がファントムタイプ(例えば、バランスのとれたツリーの深さを表す)を扱っているときに最も頻繁に起こります。 AFAIK GHCにはexistsという限定記号がありません。 existentially quantified data types(直接またはGADTのいずれかのみ)を許可します。 関数の戻り値の型で存在量の数量をシミュレートする

は例を与えるために、私はこのような機能を持っているしたいと思います:

をこれまでのところ、私は答えとして追加します2つの可能な解決策を持っている、私が知っているさせていただきます誰かがより良いことを知っているか、または異なっている場合。

答えて

10

標準的な解決策は、現存する定量データ型を作成することです。結果は次のようになります

これで、show (somethingPrintable 42)を自由に使用できます。 Exists1newtypeにすることはできません。隠しコンテキスト辞書のshowという特定の実装を回避する必要があるためです。タイプセーフなベクトルの

、一つはfromList1実装を作成するために同じように進むことができます:

{-# LANGUAGE GADTs #-} 

data Zero 
data Succ n 

data Vec a n where 
    Nil ::     Vec a Zero 
    Cons :: a -> Vec a n -> Vec a (Succ n) 

data Exists1 f where 
    Exists1 :: f a -> Exists1 f 

fromList1 :: [a] -> Exists1 (Vec a) 
fromList1 [] = Exists1 Nil 
fromList1 (x:xs) = case fromList1 xs of 
        Exists1 r -> Exists1 $ Cons x r 

これはうまく動作しますが、私が見る主な欠点は、追加のコンストラクタです。 fromList1を呼び出すたびにコンストラクタが適用され、すぐに解体されます。以前と同じようにnewtypeExists1では使用できませんが、コンパイラが許可できるタイプクラスの制約がないと思います。


私は、ランクNの継続に基づいて別のソリューションを作成しました。追加コンストラクタは必要ありませんが、追加の関数アプリケーションで同様のオーバーヘッドが追加されないかどうかはわかりません。最初のケースでは、解決策は次のようになります。

{-# LANGUAGE Rank2Types #-} 

somethingPrintable2 :: Int -> ((forall a . (Show a) => a -> r) -> r) 
somethingPrintable2 x = \c -> c x 

今1の結果を得るためにsomethingPrintable 42 showを使用します。

そして、Vecデータ型について:

{-# LANGUAGE RankNTypes, GADTs #-} 

fromList2 :: [a] -> ((forall n . Vec a n -> r) -> r) 
fromList2 [] c  = c Nil 
fromList2 (x:xs) c = fromList2 xs (c . Cons x) 

-- Or wrapped as a newtype 
-- (this is where we need RankN instead of just Rank2): 
newtype Exists3 f r = Exists3 { unexists3 :: ((forall a . f a -> r) -> r) } 

fromList3 :: [a] -> Exists3 (Vec a) r 
fromList3 []  = Exists3 (\c -> c Nil) 
fromList3 (x:xs) = Exists3 (\c -> unexists3 (fromList3 xs) (c . Cons x)) 

これは、いくつかのヘルパー関数を使用して、もう少し読みやすくすることができます。

-- | A helper function for creating existential values. 
exists3 :: f x -> Exists3 f r 
exists3 x = Exists3 (\c -> c x) 
{-# INLINE exists3 #-} 

-- | A helper function to mimic function application. 
(?$) :: (forall a . f a -> r) -> Exists3 f r -> r 
(?$) f x = unexists3 x f 
{-# INLINE (?$) #-} 

fromList3 :: [a] -> Exists3 (Vec a) r 
fromList3 []  = exists3 Nil 
fromList3 (x:xs) = (exists3 . Cons x) ?$ fromList3 xs 

私はここを参照してください主な欠点は、次のとおりです。

  1. 追加機能アプリケーションでオーバーヘッドが発生する可能性があります(コンパイラがこれをイメージ化する)。
  2. (少なくとも継続に慣れていない人では)読みにくいコードです。
関連する問題