3

から値を抽出する 2 つの関数を作成しようとしていますが、HListGHC を満足させることができないようです。

extract :: HList a -> [b]最初の関数には、リストからtype のすべての要素を抽出する署名がありbます。型にインスタンスaを持つように要求することによってのみ、それを書くことに成功しました。Typeable

class OfType a b where
    oftype :: a -> [Maybe b]

instance OfType (HList '[]) b where
    oftype = const []

instance (Typeable t, Typeable b, OfType (HList ts) b) => OfType (HList (t ': ts)) b where
    oftype (x :- xs) = (cast x :: Maybe b) : oftype xs

extract :: OfType a b => a -> [b]
extract = catMaybes . oftype

Typeable抽出のインスタンスを書き込むために制約を実際に必要としないため、これは最適ではありません。

制約で型の等号と不等号を使用しようとしましたが、インスタンスが重複するだけでした。

私が書こうとしている 2 番目の関数には、リスト内extract' :: Contains h n => HList h -> nの型の最初の要素を抽出するシグネチャがnあり、コンテキストは、リストに実際にその型の要素が 1 つ含まれていることを示しています。

制限extractなしで書くことは可能ですか?Typeable

制限extract'なしで書くことは可能ですか?Typeableどのように書くことができますContainsか?

4

2 に答える 2

3

コンパイル時に型が等しいかどうかを確認したいので、重複するインスタンスは避けられないと思います (そして、私はそれらのファンではありません...)。

また、重複するプラグマが正しいかどうか 100% 確信できるわけではありません。

{-# LANGUAGE DataKinds, TypeOperators, ScopedTypeVariables,
    MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
{-# OPTIONS -Wall #-}
module HListFilter where

import Data.HList.HList

class OfType a b where
    oftype :: a -> [b]

instance OfType (HList '[]) b where
    oftype = const []

instance {-# OVERLAPS #-} (OfType (HList ts) t) => OfType (HList (t ': ts)) t where
    oftype (HCons x xs) = x : oftype xs

instance {-# OVERLAPPABLE #-} (OfType (HList ts) b) => OfType (HList (t ': ts)) b where
    oftype (HCons _ xs) = oftype xs

test :: HList '[Int, Char, [Char], Char, Bool]
test = HCons (1::Int) (HCons 'a' (HCons "foo" (HCons 'b' (HCons True HNil))))

test_result :: [Char]
test_result = oftype test  -- "ab"
于 2016-02-07T11:47:20.123 に答える
3

András Kovács は、型ファミリのアプローチについて言及しました。これはそれを行う1つの方法です:

type family Equal (x :: *) (y :: *) where
  Equal x x = 'True
  Equal x y = 'False

type family Check (b :: *) (as :: [*]) :: [Bool] where
  Check b '[] = '[]
  Check b (a ': as) = (b `Equal` a) ': Check b as

class ps ~ Check b as =>
    OfType (ps :: [Bool]) (as :: [*]) b where
  extract :: HList as -> [b]

ここps ~ Check b asでは、タイミングの問題として、スーパークラスのコンテキストが重要です。GHC は常にインスタンスの制約をチェックする前にインスタンスにコミットしますが、スーパークラスの制約解決するまでインスタンスを見つけようとはしません。そのため、スーパークラスの制約を使用して、選択するインスタンスを修正する必要があります。

instance OfType '[] '[] b where
  extract HNil = []

instance (OfType ps as b, a ~ b) =>
           OfType ('True ': ps) (a ': as) b where
  extract (HCons x xs) = x : extract xs

instance (OfType ps as b, Equal b a ~ 'False) =>
    OfType ('False ': ps) (a ': as) b where
  extract (HCons _ xs) = extract xs

これが完了したら、「余分な」クラス パラメータを回避するインターフェイスを実際に作成できます。

class OfType' (as :: [*]) (b :: *) where
  extract' :: HList as -> [b]

instance OfType ps as b => OfType' as b where
  extract' = extract

Contains書くのはとても簡単extract'です。ただし、 の適切なインスタンスContains記述するには、 とまったく同じ種類のフープ ジャンプが必要OfTypeです。希望するクラスは次のとおりです。

class Contains xs y where
  contains :: y `Elem` xs

-- Elem is part of the dependently typed folklore.
data Elem y xs where
  Here :: Elem y (y ': xs)
  There :: Elem y xs -> Elem y (x ': xs)

しかし、インスタンスを書くと、重複した型ファミリーや閉じた型ファミリーが再び強制されます。私はこのコードをSOのどこかに書いたことを知っていますが、重複するバージョンをかなり簡単に解決できるはずです。OfType型ファミリのバージョンは、通常、 と同じパターンに従います。

于 2016-02-08T00:39:24.487 に答える